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

简要介绍了如何构建可自适应颜色、自适应且无障碍的 FAB 组件。

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

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

概览

与桌面设备相比,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 样式

为了方便起见,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 文档,焦点通常会移至顶部元素)。反转视觉顺序可为有视觉障碍的用户和键盘用户提供统一的体验,因为主要操作的样式比迷你按钮大,这向有视觉障碍的用户表明它是一项主要操作,而键盘用户会将其作为来源中的第一个项目进行聚焦。

显示了两个浮动操作按钮,开发者工具覆盖了它们的网格布局。以条纹图案显示它们之间的间隙,并显示它们计算出的高度和宽度。

.fabs {
  

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

居中是通过 place-items 处理的,而 gap 会在放置在容器中的任何 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 容器后,我们已添加 role="group"aria-label="floating action buttons",以便向屏幕阅读器用户告知他们聚焦的内容。从策略上讲,我将默认 FAB 放在最前面,以便用户首先找到主要操作。然后,我使用 flex-direction: column-reverse; 将主要按钮在底部直观地排序,以便用户轻松访问。这是一个不错的胜利,因为默认按钮在视觉上非常突出,并且对于键盘用户来说也是第一个,因此可为他们提供非常相似的体验。

最后,别忘了向屏幕阅读器用户隐藏您的图标,并确保为他们提供按钮标签,以免他们感到困惑。这已在 HTML 中完成,aria-hidden="true" 位于 <svg> 上,aria-label="Some action" 位于 <button> 上。

动画

您可以添加各种类型的动画来提升用户体验。与其他 GUI 挑战赛一样,我们将设置几个自定义属性来分别保存精简动画体验和完整动画体验的 intent。默认情况下,样式会假定用户希望减少动画效果,然后使用 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 向我发送链接,我会将其添加到下方的社区混音部分!

社区混音作品

此处尚无可显示的内容。

资源