构建悬浮操作按钮 (FAB) 组件

简要介绍了如何构建自适应颜色、响应迅速且易于访问的 FAB 组件。

在本文中,我想分享一下我对如何构建自适应颜色、响应迅速且易于访问的 FAB 组件的看法。欢迎试用演示查看源代码

如果您更喜欢视频,请观看此帖子的 YouTube 版本:

概览

与桌面设备相比,移动设备上使用 FAB 的频率更高,但在两种情况下,FAB 都很常见。主要操作在视图中显示,方便且无处不在。这种用户体验风格由 Material UI 发扬光大,您可以点击此处查看他们针对使用和放置的建议。

元素和样式

这些控件的 HTML 涉及一个容器元素以及一组(包含一个或多个)按钮。容器会在视口内放置 FAB,并管理按钮之间的间距。按钮可以是迷你版或默认版,让主要操作和次要操作之间有明显的区别。

FAB 容器

此元素可以是常规的 <div>,但为了方便盲人用户,我们可以为其添加一些实用属性,以说明此容器的用途和内容。

FAB 标记

首先创建一个 .fabs 类,以便 CSS 钩入以设置样式,然后添加 role="group"aria-label,使其不仅仅是一个通用容器,而且具有名称和用途。

<div class="fabs" role="group" aria-label="Floating action buttons">
  <!-- buttons will go here -->
</div>

FAB 样式

为方便起见,悬浮操作按钮会始终停留在视口内。这是一个非常适合使用位置信息 fixed 的用例。在该视口位置内,我选择了使用 inset-blockinset-inline,这样该位置就会与用户的文档模式协调搭配,例如从右到左或从左到右。自定义属性还用于防止重复,并确保与视口底部和侧边缘的距离相同:

.fabs {
  --_viewport-margin: 2.5vmin;

  position: fixed;
  z-index: var(--layer-1);

  inset-block: auto var(--_viewport-margin);
  inset-inline: auto var(--_viewport-margin);
}

接下来,我为容器显示屏分配 flex,并将其布局方向更改为 column-reverse。这会将子项堆叠在一起(列),并会将其视觉顺序反转。这样做的效果是将第一个可聚焦元素设置为底部元素,而不是顶部元素,每个 HTML 文档通常将焦点移至顶部。颠倒视觉顺序可为视力正常的用户和键盘用户带来统一的体验,因为如果主要操作的样式大于迷你按钮,这可向视力正常的用户表明这是主要操作,键盘用户将聚焦于来源中的第一项。

显示两个 FAB 按钮,开发者工具叠加在其网格布局上。以条纹图案显示它们之间的间隔,还显示它们的计算高度和宽度。

.fabs {
  

  display: flex;
  flex-direction: column-reverse;
  place-items: center;
  gap: var(--_viewport-margin);
}

居中处理由 place-items 处理,而 gap 会在放置在容器中的任何 FAB 按钮之间添加间距。

FAB 按钮

现在,我们需要为一些按钮设置样式,使其看起来像是浮动在所有内容之上。

默认 FAB

第一个设置样式的按钮是默认按钮。这将作为所有 FAB 按钮的基础。稍后,我们将创建一个变体,以实现另一种外观,同时尽可能少修改这些基本样式。

FAB 标记

<button> 元素是正确选择。我们先以此为基础,因为它能够带来出色的鼠标、触控和键盘用户体验。此标记最重要的方面是使用 aria-hidden="true" 向屏幕阅读器用户隐藏图标,并将必要的标签文本添加到 <button> 标记本身。在这些情况下添加标签时,我还喜欢添加 title,以便鼠标用户了解图标希望传达的信息。

<button data-icon="plus" class="fab" title="Add new action" aria-label="Add new action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>

FAB 样式

首先,我们将按钮转换为带有较深阴影的内边距圆形按钮,因为这些是按钮的第一个定义特征:

.fab {
  --_size: 2rem;

  padding: calc(var(--_size) / 2);
  border-radius: var(--radius-round);
  aspect-ratio: 1;
  box-shadow: var(--shadow-4);
}

接下来,我们来添加颜色。我们将使用之前在 GUI 挑战中用过的策略。 创建一组明确命名的自定义属性,用于静态存储浅色和深色,然后创建一个自适应自定义属性,该属性将根据用户的系统颜色偏好设置为浅色或深色变量:

.fab {
  

  /* light button and button hover */
  --_light-bg: var(--pink-6);
  --_light-bg-hover: var(--pink-7);

  /* dark button and button hover */
  --_dark-bg: var(--pink-4);
  --_dark-bg-hover: var(--pink-3);

  /* adaptive variables set to light by default */
  --_bg: var(--_light-bg);

  /* static icon colors set to the adaptive foreground variable */
  --_light-fg: white;
  --_dark-fg: black;
  --_fg: var(--_light-fg);

  /* use the adaptive properties on some styles */
  background: var(--_bg);
  color: var(--_fg);

  &:is(:active, :hover, :focus-visible) {
    --_bg: var(--_light-bg-hover);

    @media (prefers-color-scheme: dark) {
      --_bg: var(--_dark-bg-hover);
    }
  }

  /* if users prefers dark, set adaptive props to dark */
  @media (prefers-color-scheme: dark) {
    --_bg: var(--_dark-bg);
    --_fg: var(--_dark-fg);
  }
}

接下来,添加一些样式,以帮助 SVG 图标适应空间。

.fab {
  

  & > svg {
    inline-size: var(--_size);
    block-size: var(--_size);
    stroke-width: 3px;
  }
}

最后,移除按钮的点按突出显示效果,因为我们添加了自己的互动视觉反馈:

.fab {
  -webkit-tap-highlight-color: transparent;
}

迷你 FAB

本部分的目标是为 FAB 按钮创建变体。通过使部分 FAB 小于默认操作,我们可以推广用户执行频率最高的操作。

迷你 FAB 标记

HTML 与 FAB 相同,但我们添加了“.mini”类,以便 CSS 钩住变体。

<button data-icon="heart" class="fab mini" title="Like action" aria-label="Like action">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
迷你 FAB 样式

由于使用了自定义属性,因此只需调整 --_size 变量即可。

.fab.mini {
  --_size: 1.25rem;
}

两个堆叠的 FAB 按钮的屏幕截图,顶部按钮比底部按钮小。

无障碍

在 FAB 无障碍功能方面,最重要的一点是将其放置在页面键盘流中。此演示仅包含 FAB,在键盘顺序和流程方面没有任何竞争对手,这意味着它没有机会演示有意义的键盘流程。如果有多个元素争夺焦点,建议您仔细考虑用户应在该流程的什么位置进入 FAB 按钮流程。

键盘互动演示

用户聚焦到 FAB 容器后,我们已经添加了 role="group"aria-label="floating action buttons",它们会告知屏幕阅读器用户他们聚焦的内容。我有策略地将默认 FAB 放在了前面,以便用户先找到主要操作。然后,我使用 flex-direction: column-reverse; 在底部以视觉方式排列主按钮,使其靠近用户的手指,以便用户轻松访问。这是一项很棒的措施,因为默认按钮在视觉上非常醒目,也是最先吸引键盘用户的按钮,为他们带来非常相似的体验。

最后,不要忘记向屏幕阅读器用户隐藏您的图标,并确保为他们提供按钮标签,这样就不会神秘。这在 HTML 中已经完成,<svg> 上使用 aria-hidden="true"<button> 上使用 aria-label="Some action"

动画

您可以添加各种类型的动画来增强用户体验。与其他 GUI 挑战一样,我们将设置几个自定义属性来存储减少动画体验和完整动画体验的意图。默认情况下,样式会假定用户希望减少动画效果,然后使用 prefers-reduced-motion 媒体查询将过渡值切换为全动画效果。

采用自定义属性的减少动画效果策略

以下 CSS 中创建了三个自定义属性:--_motion-reduced--_motion-ok--_transition。前两个变量会根据用户的偏好设置保存适当的转换,最后一个变量 --_transition 将分别设为 --_motion-reduced--_motion-ok

.fab {
  /* box-shadow and background-color can safely be transitioned for reduced motion users */
  --_motion-reduced:
    box-shadow .2s var(--ease-3),
    background-color .3s var(--ease-3);

  /* add transform and outline-offset for users ok with motion */
  --_motion-ok:
    var(--_motion-reduced),
    transform .2s var(--ease-3),
    outline-offset 145ms var(--ease-2);

  /* default the transition styles to reduced motion */
  --_transition: var(--_motion-reduced);

  /* set the transition to our adaptive transition custom property*/
  transition: var(--_transition);

  /* if motion is ok, update the adaptive prop to the respective transition prop */
  @media (prefers-reduced-motion: no-preference) {
    --_transition: var(--_motion-ok);
  }
}

完成上述操作后,您就可以对 box-shadowbackground-colortransformoutline-offset 进行更改,从而向用户提供良好的界面反馈,让他们知道系统已收到其互动。

接下来,稍微调整 translateY,为 :active 状态增添一点个性化,这样可以为按钮带来不错的按压效果:

.fab {
  

  &:active {
    @media (prefers-reduced-motion: no-preference) {
      transform: translateY(2%);
    }
  }
}

最后,将对按钮中的 SVG 图标所做的任何更改转换为:

.fab {
  

  &[data-icon="plus"]:hover > svg {
    transform: rotateZ(.25turn);
  }

  & > svg {
    @media (prefers-reduced-motion: no-preference) {
      will-change: transform;
      transition: transform .5s var(--ease-squish-3);
    }
  }
}

总结

现在您已经知道我是如何解决的,您会怎么做? 🙂

让我们多元化我们的方法,了解在 Web 上构建的所有方式。

创建一个演示,在 Twitter 微博中发送链接,然后我会将其添加到下面的“社区混剪”部分!

社区混剪作品

此处尚无可显示的内容。

资源