SuperAPI AI 工具集

button-states

设计与创作

查看项目

更新于 10-15 16:22

技能介绍

用途

本技能用于指导按钮及各类可交互元素的状态设计。它适用于设计按钮、链接、输入框或任何可点击组件,确保每个交互组件在每种交互模式下都有完整且视觉可区分的状态。缺失或含糊的状态会让界面显得未完成,并降低用户信心。

六种状态

每个交互组件必须覆盖以下状态:

状态 触发方式 视觉信号
静止(Rest) 默认 基础色,cursor: pointer
悬停(Hover) 鼠标悬停 略微加深,背景轻微变化
按下(Active / Pressed) 鼠标按下 / 轻触 明显加深,轻微缩小
聚焦(Focus) 键盘导航 可见的聚焦环,不改变填充
禁用(Disabled) 不可用 低对比度,cursor: not-allowed,无交互
加载(Loading) 异步操作进行中 旋转指示器或脉冲,不可交互

算法化派生状态颜色

状态颜色不是独立选取的,而是从基础色出发,通过调整 HSL 亮度派生,以保证整套色板的一致性:

base:     hsl(H, S%, L%)
hover:    hsl(H, S%, L% - 8%)    ← 加深 8%
active:   hsl(H, S%, L% - 14%)   ← 加深 14%

示例:主按钮 #635BFF(hsl 243, 100%, 68%)

.btn-primary {
  background: hsl(243, 100%, 68%);       /* rest    #635BFF */
}
.btn-primary:hover {
  background: hsl(243, 100%, 60%);       /* hover   #4A40FF */
}
.btn-primary:active {
  background: hsl(243, 100%, 54%);       /* active  #3429FF */
}

对于深色背景上的浅色按钮,逻辑反转——悬停时变浅而非加深。

次要 / 描边按钮

.btn-secondary {
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-text);
}
.btn-secondary:hover {
  background: var(--color-grey-100);     /* subtle fill */
  border-color: var(--color-grey-300);
}
.btn-secondary:active {
  background: var(--color-grey-200);
}

聚焦状态

聚焦是键盘导航的要求(WCAG 2.2)。它必须可见,且不能仅依赖悬停样式——键盘用户不会触发悬停。

.btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

要点:

  • 聚焦环使用 outline 而非 box-shadow——outline 在现代浏览器中尊重 border-radius,且不影响布局。
  • outline-offset: 2–4px 让聚焦环与组件边缘有呼吸空间。
  • 绝不在没有替代聚焦样式的情况下使用 outline: none。

禁用状态

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

要点:

  • 禁用元素不受 WCAG 对比度要求约束——低透明度是正确的、有意的。
  • 使用 pointer-events: none 即使 JS 被绕过也能阻止点击事件。
  • 不要改变禁用按钮的形状或尺寸——只改变颜色和光标。

加载状态

当按钮触发异步操作时,用旋转指示器替换标签并防止重复提交。

.btn--loading {
  pointer-events: none;
  opacity: 0.7;
}

要点:

  • 加载时光标不改变。光标表达的是可供性而非进度:普通内容用箭头,可操作对象用 pointer。进度属于元素本身以及用户已经在看的加载区域,而不是鼠标恰好停留的位置。cursor: wait 在 pointer-events: none 旁边也是惰性的,因此该规则读起来是有意为之,实际不做任何事。
  • 加载期间保持按钮宽度稳定——避免标签被旋转指示器替换时发生布局偏移。
  • 完成(成功或失败)后回到静止状态。
  • 对于长时间运行的操作,配合状态消息——仅靠旋转指示器无法告诉用户正在发生什么。

按下时缩放(可选)

按下时的轻微缩小能增加物理反馈——借鉴自迪士尼的挤压原则。

.btn:active {
  transform: scale(0.97);
  transition: transform 80ms ease-out;
}

缩放值保持在 0.95–0.98 之间。低于 0.95 会让人感觉按钮要坏了。

完整按钮 CSS 参考

.btn {
  cursor: pointer;
  background: var(--color-primary);
  color: white;
  border-radius: var(--radius-button);
  padding: var(--component-padding-y-md) var(--component-padding-x-md);
  height: var(--component-height-md);
  border: none;
  transition: background 120ms ease-out, transform 80ms ease-out;
}

.btn:hover           { background: var(--color-primary-hover); }
.btn:active          { background: var(--color-primary-active); transform: scale(0.97); }
.btn:focus-visible   { outline: 2px solid var(--color-primary); outline-offset: 3px; }
.btn:disabled        { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
.btn.btn--loading    { opacity: 0.7; pointer-events: none; }

评审清单

  • 每个交互元素是否都定义了全部六种状态?
  • 悬停和按下颜色是否通过亮度调整从基础色派生(而非随意选取)?
  • 聚焦状态是否可见并使用 outline(未被移除)?
  • 禁用状态是否为低透明度并配合 cursor: not-allowed?
  • 加载状态是否防止重复提交?
  • 过渡时长是否为 80–150ms——不是瞬间,也不缓慢?
  • 所有交互元素在静止状态下是否都显示 cursor: pointer?
  • 操作运行期间光标是否保持不变,进度由元素自身显示?