如今,完全可以构建自定义复选框、单选按钮和切换开关,同时保持语义和可访问性。我们甚至不需要一行 JavaScript 或额外的 HTML 元素!事实上,最近比过去更容易了。让我们来看看。
以下是我们的最终目标
事情确实比以前更容易了!
原因是,我们终于可以为<input>
标签本身的::before
和::after
伪元素设置样式了。这意味着我们可以保留和设置<input>
的样式,并且不需要任何额外的元素。以前,我们必须依赖额外的<div>
或<span>
来实现自定义设计。
让我们看看 HTML
这里没什么特别的。我们只需使用这段 HTML 就可以设置输入的样式
<!-- Checkbox -->
<input type="checkbox">
<!-- Radio -->
<input type="radio">
<!-- Switch -->
<input type="checkbox" class="switch">
这就是 HTML 部分,当然,建议使用 name 和 id 属性,以及匹配的 <label>
元素
<!-- Checkbox -->
<input type="checkbox" name="c1" id="c1">
<label for="c1">Checkbox</label>
<!-- Radio -->
<input type="radio" name="r1" id="r1">
<label for="r1">Radio</label>
<!-- Switch -->
<input type="checkbox" class="switch" name="s1" id="s1">
<label for="s1">Switch</label>
开始设置样式
首先,我们检查对appearance: none;
的支持,包括它的前缀伴侣。appearance
属性是关键,因为它旨在从元素中删除浏览器的默认样式。如果属性不受支持,则样式将不会应用,并将显示默认的输入样式。这完全没问题,并且是渐进增强发挥作用的一个很好的例子。
@supports(-webkit-appearance: none) or (-moz-appearance: none) {
input[type='checkbox'],
input[type='radio'] {
-webkit-appearance: none;
-moz-appearance: none;
}
}
就目前而言,appearance 是一个工作草案,但以下是支持情况
此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
83* | 80 | 否 | 83* | 15.4 |
移动/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 127 | 15.4 |
与链接一样,我们必须考虑表单元素的不同交互状态。我们在设置元素样式时将考虑这些因素
:checked
:hover
:focus
:disabled
例如,以下是如何设置切换输入的样式,创建旋钮并考虑:checked
状态
/* The toggle container */
.switch {
width: 38px;
border-radius: 11px;
}
/* The toggle knob */
.switch::after {
left: 2px;
top: 2px;
border-radius: 50%;
width: 15px;
height: 15px;
background: var(--ab, var(--border));
transform: translateX(var(--x, 0));
}
/* Change color and position when checked */
.switch:checked {
--ab: var(--active-inner);
--x: 17px;
}
/* Drop the opacity of the toggle knob when the input is disabled */
.switch:disabled:not(:checked)::after {
opacity: .6;
}
我们使用<input>
元素作为容器。输入内的旋钮是使用::after
伪元素创建的。再次强调,不需要额外的标记!
如果您打开演示中的样式,您会发现我们正在定义一些 CSS 自定义属性,因为这已经成为在样式表中管理可复用值的不错方法
@supports(-webkit-appearance: none) or (-moz-appearance: none) {
input[type='checkbox'],
input[type='radio'] {
--active: #275EFE;
--active-inner: #fff;
--focus: 2px rgba(39, 94, 254, .25);
--border: #BBC1E1;
--border-hover: #275EFE;
--background: #fff;
--disabled: #F6F8FF;
--disabled-inner: #E1E6F9;
}
}
但我们使用自定义属性还有另一个原因——它们非常适合根据元素状态更新值!我们这里不会详细介绍,但以下是如何使用自定义属性来设置不同状态的示例。
/* Default */
input[type='checkbox'],
input[type='radio'] {
--active: #275EFE;
--border: #BBC1E1;
border: 1px solid var(--bc, var(--border));
}
/* Override defaults */
input[type='checkbox']:checked,
input[type='radio']:checked {
--b: var(--active);
--bc: var(--active);
}
/* Apply another border color on hover if not checked & not disabled */
input[type='checkbox']:not(:checked):not(:disabled):hover,
input[type='radio']:not(:checked):not(:disabled):hover {
--bc: var(--border-hover);
}
为了可访问性,我们应该添加一个自定义焦点样式。我们删除了默认的轮廓,因为它不能像我们设置样式的其他元素一样圆角化。但是,圆角和阴影可以创造出与轮廓一样的圆角样式。
input[type='checkbox'],
input[type='radio'] {
--focus: 2px rgba(39, 94, 254, .25);
outline: none;
transition: box-shadow .2s;
}
input[type='checkbox']:focus,
input[type='radio']:focus {
box-shadow: 0 0 0 var(--focus);
}
还可以对<label>
元素进行对齐和样式设置,该元素在 HTML 中直接位于<input>
元素之后
<input type="checkbox" name="c1" id="c1">
<label for="c1">Checkbox</label>
input[type='checkbox'] + label,
input[type='radio'] + label {
display: inline-block;
vertical-align: top;
/* Additional styling */
}
input[type='checkbox']:disabled + label,
input[type='radio']:disabled + label {
cursor: not-allowed;
}
以下是那个演示
希望您已经看到了如今创建自定义表单样式有多么方便。由于直接位于表单输入的伪元素,它需要的标记更少。由于自定义属性,它需要的复杂样式切换更少。而且由于@supports
,它具有相当不错的浏览器支持。
总而言之,这比我们过去不得不应对的开发体验要好得多!
我注意到你没有使用 :-)
这仍然不容易(至少没有好的方法)。
您好,您能提供您这句话的参考资料吗?
„原因是,我们终于可以为标签本身的::before 和::after 伪元素设置样式了。“
我只找到了 Stack Overflow 上的一个帖子…
是的,我也很好奇。浏览器什么时候开始支持这个功能,我们如何检查输入的伪元素支持情况?
浏览器已经支持这个功能两年多了。这就是我构建Native Elements的方式
https://native-elements.stackblitz.io
关于这个主题的信息很少——很遗憾…
如果
appearance: none;
也支持,则应该支持它,所以我们得到了一个盒子模型很高兴我们可以使用
@supports
检查支持情况是的,更多关于这方面的资料会很棒!
似乎只对某些输入(如单选按钮和复选框)有效,但对其他输入(如文本和按钮)无效。
很棒的演示,Aaron。但是,对于切换开关,我个人仍然会按照包容性组件或Mozilla 文档中所述的方式来处理它们。
我担心我无法通过您的切换开关,因为它不可拖动。尽管我很喜欢这种简便的方式,没有额外的标记,没有奇怪的样式技巧。
通过添加结束标签()将输入转换为容器元素不是有效的 HTML。浏览器可能由于某种原因接受了它,但您的文档将无法验证。
我一直渴望能够将伪元素插入非容器元素(如图像或输入)的日子,但似乎我们还得等等,除非我们准备编写非法的标记。我个人不会这么做…
好的,但我没有看到这篇文章中出现这种情况,对吧?
存在未闭合的输入(完全有效),例如
但不存在“容器”输入,我同意,它们是无效的,就像…
抱歉!我查看了检查器中的生成代码,在代码中,输入有一个结束标签。看来,当有人将内容插入非容器元素时,浏览器会这样做。
吸取的教训:永远不要过度信任代码检查器…
嘿!我将您的代码分叉到 React 组件。看看吧!
https://codesandbox.io/s/custom-css-only-form-inputs-react-hofx5
在 Microsoft Edge 44 中不起作用——我不知道为什么。Bootstrap 的自定义复选框和单选按钮在 Edge 44 中有效:https://bootstrap.ac.cn/docs/4.4/components/forms/#checkboxes-and-radios-1
其他人也在 Edge 中遇到了这个问题:https://stackoverflow.com/questions/53657488/how-do-i-make-this-checkbox-styling-work-in-edge
如果想要像 Bootstrap 一样使用更小的控件,就会遇到一个问题。如果将控件的高度从 21px 降低到 16px,那么在开启 Windows 缩放功能的显示器上,单选按钮的伪影就会变得可见。我已经在下面链接中描述了这个问题:https://stackoverflow.com/questions/61760484/unable-to-center-an-element-when-windows-scaling-is-enabled-125
你也可以在这里观察这个问题:https://codepen.io/iwis/pen/eYpjeYr - 这是添加了按钮的原始代码。不过,这里没有问题,因为控件更大 (21px),所以单选按钮没有那么扭曲。
对于较小的控件,这个问题该如何解决呢?
嗨,Aaron,
非常感谢你分享这篇有帮助的文章。我复制了 CodePen 来学习它是如何工作的。在这个过程中,我发现了一些有用的修改,我想分享一下。欢迎反馈。我还有一个问题。
复选框和单选按钮的内容都是空字符串。对于类型为“checkbox”的输入,:after 的默认内容是复选标记吗?类似地,单选按钮的默认内容是方框吗?如果是这样,请告诉我哪里有相关文档,这样我就可以阅读一下它的工作原理。
这是我的 CodePen,希望对大家有所帮助:https://codepen.io/greatgraphicdesign/pen/MWaLrWp
我做出的修改...
- 移除自定义属性,因为它们使我难以理解代码。
- 将 CSS 重置代码放在 @support 前面,因为我通常会使用这种方式。
- 添加了 .focus 和 tabIndex 用于可访问性。(据我所知,轮廓仅在获得焦点时出现。)
- 为了清晰起见,移除了 ul/li 标签。通过这种方式,我发现了对 vertical-align: middle 的需求。
- 将标签包裹在项目周围。这仅仅是我的个人喜好,因为这些类型的输入。你现在的做法可以让你对光标进行样式设置,这很酷。你仍然需要一个脚本来添加/删除 disabled 属性。我正在使用 .point 类来标记已启用的项目标签,这个类也需要用脚本添加/删除。
再次感谢你发布这篇博文!