构建“设置”组件

有关如何构建滑块和复选框的设置组件的基本概览。

在这篇博文中,我想和您分享一下如何针对 一种响应式网页,支持多种设备输入, 。试用演示版

<ph type="x-smartling-placeholder">
</ph>
演示

如果您更喜欢视频,或者想要预览我们正在构建的内容的界面/用户体验,可以参考以下资源 YouTube 上的简短演示:

概览

我将该组件的各个方面分为以下几部分:

  1. 布局
  2. 颜色
  3. 自定义范围输入
  4. 自定义复选框输入
  5. 无障碍功能注意事项
  6. JavaScript

布局

这是第一个面向所有 CSS 网格的 GUI 挑战演示!以下是每个网格 Chrome DevTools 网格图标突出显示:

彩色轮廓和间隔间距叠加层有助于显示构成设置布局的所有框

CANNOT TRANSLATE

最常见的布局:

foo {
  display: grid;
  gap: var(--something);
}

我将这种布局称为“仅仅为了间隔”因为它仅使用网格在图块之间添加间距。

有五种布局采用此策略,下面列出了所有布局:

通过轮廓突出显示且已填满间隙的垂直网格布局

包含每个输入组 (.fieldset-item) 的 fieldset 元素使用 gap: 1px 来 在元素之间创建细线边框。没有棘手的边界解决方案!

已填满间隙
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
边框特效
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

自然网格封装

最复杂的布局最终是宏布局,即逻辑布局 介于 <main><form> 之间。

居中封装内容

Flexbox 和网格都提供 align-itemsalign-content,在处理封装元素时,使用 content 布局 对齐方式将作为一个组在子元素之间分配空间。

main {
  display: grid;
  gap: var(--space-xl);
  place-content: center;
}

主要元素使用 place-content: center 对齐方式 简写形式 子项在单列和双列布局中垂直和水平居中。

请观看上面的视频,了解“内容”即使封装了 发生了什么情况。

重复自动调整最小值最大值

<form> 针对每个版块使用自适应网格布局。 此布局会根据可用空间从一列切换到两列。

form {
  display: grid;
  gap: var(--space-xl) var(--space-xxl);
  grid-template-columns: repeat(auto-fit, minmax(min(10ch, 100%), 35ch));
  align-items: flex-start;
  max-width: 89vw;
}

此网格的“row-gap”(--space-xl) 值与“column-gap”(--space-xxl) 不同 以便在自适应布局上添加自定义触摸元素。当这些列堆叠时, 但又不像在宽屏设备上那样大

grid-template-columns 属性使用 3 个 CSS 函数:repeat()minmax()min()Una Kravets 有一个很棒的布局博客 介绍这方面的内容 RAM

与 Una 的布局相比,我们的布局中新增了 3 项特殊功能:

  • 我们传递一个额外的 min() 函数。
  • 我们指定 align-items: flex-start
  • max-width: 89vw 样式。

Evan Minto 在他们的博客上对这个额外的 min() 函数进行了详细说明, post 使用 minmax() 和 min()。 我建议你读一读。flex-start 对齐校正是 删除默认的拉伸效果,这样该布局的子项便不会 它们的高度需要相等,它们可以具有自然的固有高度。通过 YouTube 视频对此新增对齐方式进行了简要介绍。

不妨在本文中对 max-width: 89vw 进行一小段分析。 下面我来看看应用样式和不应用样式时的布局:

这是怎么回事?指定 max-width 时会提供上下文, 明确调整大小或确定 auto-fit 的尺寸 布局算法,以了解 虽然显而易见的是 空间为“全宽”,根据 CSS 网格规范,必须指定 资源。我提供了一个 max-size。

所以,为什么选择 89vw?因为“它有效”来调整我的布局 我和其他几个 Chrome 浏览器团队的成员正在调查 像 100vw 这样是不够的,如果这确实是 bug。

间距

这种布局大部分的和谐来自于有限的间距调色板, 7

:root {
  --space-xxs: .25rem;
  --space-xs:  .5rem;
  --space-sm:  1rem;
  --space-md:  1.5rem;
  --space-lg:  2rem;
  --space-xl:  3rem;
  --space-xxl: 6rem;
}

将这些流程完美地与网格、CSS @nest5 级语法 @media。下面是一个完整的 <main> 布局样式集示例。

main {
  display: grid;
  gap: var(--space-xl);
  place-content: center;
  padding: var(--space-sm);

  @media (width >= 540px) {
    & {
      padding: var(--space-lg);
    }
  }

  @media (width >= 800px) {
    & {
      padding: var(--space-xl);
    }
  }
}

内容居中的网格,默认情况下有适度内边距(如同在移动设备上)。但是 随着视口空间的增加,它通过增加内边距来扩展。 2021 年的 CSS 看起来相当不错!

还记得之前的布局“仅仅用于间隔”吗?下面是更完整的 它们在此组件中的外观:

header {
  display: grid;
  gap: var(--space-xxs);
}

section {
  display: grid;
  gap: var(--space-md);
}

颜色

有节制地使用颜色,帮助这种设计脱颖而出,但又极具表现力 极简。具体操作步骤如下:

:root {
  --surface1: lch(10 0 0);
  --surface2: lch(15 0 0);
  --surface3: lch(20 0 0);
  --surface4: lch(25 0 0);

  --text1: lch(95 0 0);
  --text2: lch(75 0 0);
}

我用数字来命名表面和文本颜色,而不是像这样 surface-darksurface-darker,因为在媒体查询中, 浅色和深色没有意义

我可以在偏好设置媒体查询中切换它们,如下所示:

:root {
  ...

  @media (prefers-color-scheme: light) {
    & {
      --surface1: lch(90 0 0);
      --surface2: lch(100 0 0);
      --surface3: lch(98 0 0);
      --surface4: lch(85 0 0);

      --text1: lch(20 0 0);
      --text2: lch(40 0 0);
    }
  }
}

请务必先快速了解整体情况和策略 我们将深入介绍颜色语法细节但是,因为我比自己快, 让我稍微备份一下。

LCH?

无需深入研究颜色理论,LCH 就是一种以人为本的语法, 而不是用数学计算来测量颜色的方式(例如 255)。这赋予它明显的优势,因为人类可以更轻松地编写代码, 人类会随之调整。

<ph type="x-smartling-placeholder">
</ph> pod.link/csspodcast 网页的屏幕截图,其中显示“颜色 2:感知”剧集 <ph type="x-smartling-placeholder">
</ph> 访问 CSS 播客,了解感知颜色(以及更多内容!)

今天,在这个演示中,我们重点了解一下语法和 来呈现明暗变化我们来看看 1 种表面和 1 种文本颜色:

:root {
  --surface1: lch(10 0 0);
  --text1:    lch(95 0 0);

  @media (prefers-color-scheme: light) {
    & {
      --surface1: lch(90 0 0);
      --text1:    lch(40 0 0);
    }
  }
}

--surface1: lch(10 0 0) 可转换为 10% 亮度、0 色度和 0 色相:a 就是深无色的灰色然后,在浅色模式的媒体查询中, 已使用 --surface1: lch(90 0 0); 翻转为 90%。以上就是 策略首先只更改 2 个主题之间的亮度,同时保持 设计所需的对比度或可保持无障碍功能的对比度。

这里使用 lch() 的好处是,亮度是人性化的,我们能 对它进行 % 更改是一件好事,它会从感知上持续显示, 与%不同。例如,hsl() 不是 可靠性

还有 了解 颜色空间和 lch()。即将推出!

CSS 目前根本无法使用这些颜色。 再强调一遍:在当今的大多数现代市场中,我们根本没有 监控。这些不是任何一种颜色,而是最鲜明的颜色 可以显示的内容。监控硬件的演变导致我们的网站被淘汰 速度比 CSS 规范和浏览器实施更快。

Lea Verou

具有配色方案的自适应表单控件

许多浏览器都提供深色主题控件,目前是 Safari 和 Chromium,不过 必须在 CSS 或 HTML 中指定您的设计使用它们。

上图显示了 属性在 样式面板中的效果, 开发者工具。该演示使用了 HTML 标记,我认为该标记通常为 更好的位置:

<meta name="color-scheme" content="dark light">

如需了解详情,请参阅这篇color-scheme 文章作者:Thomas Steiner。还有很多有待收获 与深色复选框输入相比!

CSS accent-color

最近有 活动 对表单元素执行 accent-color 操作,这是一种 CSS 样式,可更改 用于浏览器输入元素中的着色颜色。有关详情,请点击此处 GitHub。我已将其加入我的 样式。只要浏览器支持,复选框就会变为 以粉色和紫色为主色。

input[type="checkbox"] {
  accent-color: var(--brand);
}

Chromium 上粉色复选框的屏幕截图(在 Linux 上)

采用固定的渐变效果和内部聚焦的局部彩色效果

谨慎使用彩色效果最明显,这是我想要实现的一种方式 那就是通过丰富多彩的界面互动来实现

上面的视频中有许多层的界面反馈和互动,它们通过以下方式为互动赋予个性:

  • 突出显示背景信息。
  • 提供“如何完全”的界面反馈值在范围内。
  • 提供界面反馈,表明某个字段正在接受输入。

为了在用户与某个元素互动时提供反馈,CSS 会使用 :focus-within 伪类来更改各种元素的外观,下面我们来详细介绍 .fieldset-item,这也非常有趣:

.fieldset-item {
  ...

  &:focus-within {
    background: var(--surface2);

    & svg {
      fill: white;
    }

    & picture {
      clip-path: circle(50%);
      background: var(--brand-bg-gradient) fixed;
    }
  }
}

当此元素的其中一个子元素在范围内时:

  1. .fieldset-item 背景指定了较高的对比度表面颜色。
  2. 为了提高对比度,嵌套的 svg 会填充白色。
  3. 嵌套的 <picture> clip-path 会展开为一个完整圆圈, 使用明亮的固定渐变填充背景。

自定义范围

根据下面的 HTML 输入元素,我将向您展示如何自定义其 外观:

<input type="range">

我们需要自定义此元素包含 3 个部分:

  1. 范围元素 / 容器
  2. 跟踪
  3. 拇指

范围元素样式

input[type="range"] {
  /* style setting variables */
  --track-height: .5ex;
  --track-fill: 0%;
  --thumb-size: 3ex;
  --thumb-offset: -1.25ex;
  --thumb-highlight-size: 0px;

  appearance: none;         /* clear styles, make way for mine */
  display: block;
  inline-size: 100%;        /* fill container */
  margin: 1ex 0;            /* ensure thumb isn't colliding with sibling content */
  background: transparent;  /* bg is in the track */
  outline-offset: 5px;      /* focus styles have space */
}

前几行 CSS 是样式的自定义部分 清楚标记它们会有所帮助。其余样式大多已重置样式, 为构建复杂的组件部分提供了一致的基础。

轨道样式

input[type="range"]::-webkit-slider-runnable-track {
  appearance: none; /* clear styles, make way for mine */
  block-size: var(--track-height);
  border-radius: 5ex;
  background:
    /* hard stop gradient:
        - half transparent (where colorful fill we be)
        - half dark track fill
        - 1st background image is on top
    */
    linear-gradient(
      to right,
      transparent var(--track-fill),
      var(--surface1) 0%
    ),
    /* colorful fill effect, behind track surface fill */
    var(--brand-bg-gradient) fixed;
}

诀窍就是“揭露”填充颜色您可以通过 强制停止渐变效果渐变效果在填充百分比之前是透明的, (未填充的轨道表面颜色)。这个未填充的表面后面 全角颜色,等待透明度显示出来。

轨迹填充样式

我的设计需要使用 JavaScript 才能保持填充样式。那里 是仅限 CSS 的策略,但要求 Thumb 元素高度相同 我没能在这些极限内找到和谐的曲目

/* grab sliders on page */
const sliders = document.querySelectorAll('input[type="range"]')

/* take a slider element, return a percentage string for use in CSS */
const rangeToPercent = slider => {
  const max = slider.getAttribute('max') || 10;
  const percent = slider.value / max * 100;

  return `${parseInt(percent)}%`;
};

/* on page load, set the fill amount */
sliders.forEach(slider => {
  slider.style.setProperty('--track-fill', rangeToPercent(slider));

  /* when a slider changes, update the fill prop */
  slider.addEventListener('input', e => {
    e.target.style.setProperty('--track-fill', rangeToPercent(e.target));
  })
})

我认为这样在视觉效果方面非常棒。无需任何操作, JavaScript 中,--track-fill 属性不是必需的,只是没有 填充样式。如果可以使用 JavaScript,请填充自定义 属性,同时观察用户的任何更改,将自定义属性与 值。

这里有一个不错的 发布 Ana 制作的 CSS-Tricks Tudor,演示了适用于 曲目填充。我还找到了以下结果 range 元素非常鼓舞人心。

拇指样式

input[type="range"]::-webkit-slider-thumb {
  appearance: none; /* clear styles, make way for mine */
  cursor: ew-resize; /* cursor style to support drag direction */
  border: 3px solid var(--surface3);
  block-size: var(--thumb-size);
  inline-size: var(--thumb-size);
  margin-top: var(--thumb-offset);
  border-radius: 50%;
  background: var(--brand-bg-gradient) fixed;
}

这些样式大多用于形成一个漂亮的圆圈。 同样,您会看到固定的背景渐变, 动态配色的滑块、滑道和相关联的 SVG 元素。 我区分了互动的样式,以帮助隔离 box-shadow 悬停突出显示所用的技术:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

::-webkit-slider-thumb {
  

  /* shadow spread is initally 0 */
  box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color);

  /* if motion is OK, transition the box-shadow change */
  @media (--motionOK) {
    & {
      transition: box-shadow .1s ease;
    }
  }

  /* on hover/active state of parent, increase size prop */
  @nest input[type="range"]:is(:hover,:active) & {
    --thumb-highlight-size: 10px;
  }
}

我们的目标是提供易于管理且具有动画效果的视觉亮点,以方便用户提供反馈。 通过使用方框阴影,可以避免触发 layout。我这样做 制作出一个既不模糊的阴影,又与 thumb 元素。然后,我会在鼠标指针悬停时更改并转换其展开尺寸。

要是能在复选框上这么轻松地突出显示效果...

跨浏览器选择器

我发现我需要这些 -webkit--moz- 选择器来实现跨浏览器 一致性:

input[type="range"] {
  &::-webkit-slider-runnable-track {}
  &::-moz-range-track {}
  &::-webkit-slider-thumb {}
  &::-moz-range-thumb {}
}

自定义复选框

根据下面的 HTML 输入元素,我将向您展示如何自定义其 外观:

<input type="checkbox">

我们需要自定义此元素包含 3 个部分:

  1. 复选框元素
  2. 关联的标签
  3. 突出显示效果

复选框元素

input[type="checkbox"] {
  inline-size: var(--space-sm);   /* increase width */
  block-size: var(--space-sm);    /* increase height */
  outline-offset: 5px;            /* focus style enhancement */
  accent-color: var(--brand);     /* tint the input */
  position: relative;             /* prepare for an absolute pseudo element */
  transform-style: preserve-3d;   /* create a 3d z-space stacking context */
  margin: 0;
  cursor: pointer;
}

transform-styleposition 样式为我们稍后介绍的伪元素做准备 设置突出显示样式。否则,大多数情况下 我的一些小主意的风格我喜欢把光标作为指针 边框偏移,默认复选框过小,accent-color 支持,请将这些 复选框。

复选框标签

为复选框提供标签很重要,原因有两个。第一个是 表示复选框值的用途, 其次,对于用户体验而言,Web 用户已经习惯 选中相应复选框

输入
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
标签
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

在您的标签上,放置一个指向复选框的 for 属性(按 ID:<label for="text-notifications">)。在复选框上,将名称和 ID 双倍, 确保使用各种工具和技术(例如鼠标或屏幕阅读器)找到它: <input type="checkbox" id="text-notifications" name="text-notifications">。 通过连接网络可以免费使用 :hover:active 等,这提高了 用户可通过哪些方式与表单互动

复选框突出显示

我希望使界面保持一致,并且滑块元素具有很好的 与复选框一起使用的突出显示缩略图缩略图原为 能够使用 box-shadowspread 属性放大阴影, 。不过,这种效果在这里不起作用,因为我们的复选框是、并且应该 be、方形。

我使用伪元素实现了同样的视觉效果, 不幸的是,大量 CSS 十分棘手:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

input[type="checkbox"]::before {
  --thumb-scale: .01;                        /* initial scale of highlight */
  --thumb-highlight-size: var(--space-xl);

  content: "";
  inline-size: var(--thumb-highlight-size);
  block-size: var(--thumb-highlight-size);
  clip-path: circle(50%);                     /* circle shape */
  position: absolute;                         /* this is why position relative on parent */
  top: 50%;                                   /* pop and plop technique (https://web.dev/centering-in-css#5-pop-and-plop) */
  left: 50%;
  background: var(--thumb-highlight-color);
  transform-origin: center center;            /* goal is a centered scaling circle */
  transform:                                  /* order here matters!! */
    translateX(-50%)                          /* counter balances left: 50% */
    translateY(-50%)                          /* counter balances top: 50% */
    translateZ(-1px)                          /* PUTS IT BEHIND THE CHECKBOX */
    scale(var(--thumb-scale))                 /* value we toggle for animation */
  ;
  will-change: transform;

  @media (--motionOK) {                       /* transition only if motion is OK */
    & {
      transition: transform .2s ease;
    }
  }
}

/* on hover, set scale custom property to "in" state */
input[type="checkbox"]:hover::before {
  --thumb-scale: 1;
}

创建圆形伪元素很简单,但将其放置 更加难以处理。这里是之前和 修复后:

这绝对是一种微互动,但对我来说,保持视觉元素 一致性动画缩放技术和 。我们将自定义属性设置为新值,然后让 CSS 对其进行转换 进行微调。此处的关键功能是 translateZ(-1px)。通过 父项创建了一个 3D 空间,这个伪元素子项通过 将自身稍微后退在 Z 轴空间中。

无障碍

YouTube 视频很好地展示了鼠标、键盘和 此设置组件的屏幕阅读器互动。我会指出一些 。

HTML 元素选项

<form>
<header>
<fieldset>
<picture>
<label>
<input>

每个文件都包含有关用户浏览工具的提示和提示。部分元素 提供互动提示,有的将互动联系起来,而有的则有助于塑造 屏幕阅读器导航到的无障碍树

HTML 属性

我们可以隐藏屏幕阅读器不需要的元素, 在此例中是滑块旁边的图标:

<picture aria-hidden="true">

以上视频演示了 Mac OS 上的屏幕阅读器流程。请注意 焦点会直接从一个滑块移动到下一个滑块。这是因为 图标。不使用 属性时,用户需要停下来听一听,然后跳过这张照片, 他们可能看不到的内容

SVG 涉及到一系列数学元素,让我们添加一个 <title> 元素来实现免费的鼠标悬停 以及关于数学内容生成的直观易懂的评论:

<svg viewBox="0 0 24 24">
  <title>A note icon</title>
  <path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
</svg>

除此之外,我们还使用了足够的明确标记的 HTML,以便表单测试 在鼠标、键盘、视频游戏控制器和屏幕阅读器上都能实现出色性能。

JavaScript

我已经介绍了如何通过 JavaScript 管理滑道填充颜色 现在,我们来看看与 <form> 相关的 JavaScript:

const form = document.querySelector('form');

form.addEventListener('input', event => {
  const formData = Object.fromEntries(new FormData(form));
  console.table(formData);
})

每当有人与表单互动和更改表单时,控制台都会将表单记录为 将对象放入表中,以便于在提交到服务器之前进行检查。

console.table() 结果的屏幕截图,其中表单数据显示在表中

总结

现在你知道我怎么做到的了,你会怎么做?!这样很有趣 组件架构!谁会制作在他们的 您最喜欢的框架是什么?🙂

让我们一起采用多样化的方法,学习所有在 Web 上构建应用的方法。 创建演示,在 Twitter 微博上添加链接,然后我会添加 到下方的社区混剪部分!

社区混剪作品

  • @tomayac 分享自己在 悬停区域!此版本之间没有悬停间隙 元素:demo来源