简要介绍如何构建滑块和复选框的设置组件。
在这篇博文中,我想分享一个关于为 Web 构建自适应、支持多种设备输入且可跨浏览器运行的“设置”组件的想法。试用演示。
如果您更喜欢视频,或者想预览我们正在构建的内容,可以参考下面这段简短的 YouTube 演示:
概览
我将这个组件的方面分成了以下几个部分:
布局
这是第一个全部 CSS 网格的 GUI 挑战演示!以下是使用适用于网格的 Chrome 开发者工具突出显示的各个网格:
仅作空档
最常见的布局:
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 和 grid 都提供 align-items
或 align-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 在其博客中的博文使用 minmax() 和 min() 实现固有响应的 CSS 网格中详细描述了这项额外的 min()
函数。建议您阅读本文。flex-start
对齐校正是移除默认的拉伸效果,这样该布局的子项就无需具有相同的高度,而可以拥有自然的固有高度。YouTube 视频对此次对齐方式的新增内容进行了快速解析。
在本博文中,我们会对 max-width: 89vw
做一个小小的细分。我来看看应用样式和未应用样式的布局:
这是怎么回事?指定 max-width
后,它会为 auto-fit
布局算法提供上下文、显式尺寸或确定尺寸,以了解空间可以重复多少次。虽然很明显,相应空间是“全宽”的,但根据 CSS 网格规范,必须提供确切的大小或最大大小。我提供了最大尺寸。
那么,为什么是 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 @nest 和 @media 的 5 级语法可以非常有效地使用这些 flow。下面是一个示例,即完整的 <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 看起来还不错!
还记得之前的布局“Just for gap”吗?下面是它们在此组件中外观的更完整版本:
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-dark
和 surface-darker
等名称来为 Surface 颜色和文本颜色命名,因为在媒体查询中,我会翻转这些颜色,而浅色和深色没有意义。
我在偏好设置媒体查询中将其翻转过来,如下所示:
: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)。这具有独特的优势,因为人类可以更轻松地书写,而其他人类也会适应这些调整。
![pod.link/csspodcast 网页的屏幕截图,其中调出颜色 2:感知剧集](https://web.developers.google.cn/static/articles/building/a-settings-component/image/a-screenshot-podlinkcs-a40f130bf81b8.png?authuser=6&hl=uk)
今天,在本演示中,我们重点介绍一下语法以及我想要通过翻转来显示浅色和深色的值。我们来看一种 Surface 和 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 色调:非常深的无色灰色。然后,在浅色模式的媒体查询中,使用 --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">
如需了解所有相关信息,请参阅 Thomas Stteiner 的这篇 color-scheme
文章。与深色复选框输入相比,效果更佳!
CSS accent-color
近期有一项关于表单元素的 accent-color
活动,它是一种 CSS 样式,可以更改浏览器输入元素中使用的色调颜色。如需了解详情,请访问 GitHub。我已将其添加到此组件的样式中由于浏览器支持,因此我的复选框会更偏向于粉色和紫色弹出主题。
input[type="checkbox"] {
accent-color: var(--brand);
}
使用固定渐变和聚焦内效果的局部彩色功能
谨慎使用时,颜色最显眼,我喜欢通过彩色界面互动来实现这一目标。
上述视频包含多层界面反馈和互动层,它们有助于为互动赋予个性化元素,具体体现在以下方面:
- 突出显示背景信息。
- 提供值在范围内的“完整程度”的界面反馈。
- 提供界面反馈,指出字段正在接受输入。
为了在与元素互动时提供反馈,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;
}
}
}
当此元素的某个子项具有焦点范围时:
- 系统为
.fieldset-item
背景分配了对比较高的 Surface 颜色。 - 嵌套的
svg
为白色填充,以提高对比度。 - 嵌套的
<picture>
clip-path
会展开为一个完整圆圈,背景则用明亮的固定渐变进行填充。
自定义范围
鉴于以下 HTML 输入元素,我将向您展示如何自定义其外观:
<input type="range">
我们需要自定义此元素 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 的策略,但它们要求拇指元素与轨道的高度相同,但我无法在这些限制内找到平衡。
/* 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 Toudor 撰写的有关 CSS-Tricks 的超棒博文介绍了一种仅使用 CSS 来实现轨道填充的解决方案。这个 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;
}
}
其目标是打造一个易于管理且以动画形式突出显示的用户反馈。 通过使用方框阴影,我可以避免使用该效果触发布局。为此,我要创建一个不模糊且与拇指元素的圆形形状匹配的阴影。然后更改并转换其悬停大小。
如果复选框上的突出显示效果这么简单...
跨浏览器选择器
我发现我需要使用这些 -webkit-
和 -moz-
选择器来实现跨浏览器一致性:
input[type="range"] {
&::-webkit-slider-runnable-track {}
&::-moz-range-track {}
&::-webkit-slider-thumb {}
&::-moz-range-thumb {}
}
自定义复选框
鉴于以下 HTML 输入元素,我将向您展示如何自定义其外观:
<input type="checkbox">
我们需要自定义此元素 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-style
和 position
样式会为我们稍后介绍用于设置突出显示效果的伪元素做准备。除此之外,我说的大多是我喜欢的未成年人主观观点我喜欢光标变成指针,我喜欢轮廓偏移,默认复选框太小,如果支持 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>
在您的标签上,放置一个按 ID 指向复选框的 for
属性:<label for="text-notifications">
。将复选框的名称和 ID 翻倍,以确保可通过不同的工具和技术(例如鼠标或屏幕阅读器)找到它:<input type="checkbox" id="text-notifications" name="text-notifications">
。
通过连接可免费使用 :hover
、:active
等,从而增加了用户与表单的互动方式。
突出显示复选框
我想保持界面一致性,并且滑块元素有一个很棒的缩略图突出显示效果,我想与复选框一起使用。缩略图可以使用 box-shadow
,而 spread
属性则可放大和缩小阴影。不过,这种效果不适用于此处,因为我们的复选框并且应该为方形。
我可以使用伪元素来实现相同的视觉效果,但遗憾的是,我还使用了一些复杂的 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);
})
每次与表单互动并更改表单时,控制台都会将表单作为对象记录到表中,以便在提交到服务器之前轻松检查。
总结
现在你已经知道我是怎么做的,你知道该怎么做呢?!这就是一些有趣的组件架构!谁将构建在其喜爱的框架中包含槽的第一个版本?🙂
下面,我们就来介绍一下我们的方法多样化,并了解在 Web 上构建网站的所有方法。 创建一个演示,点击 tweet me 链接,我就会将其添加到下面的社区混剪部分!