使用现代 CSS 特性自定义表单输入样式

Avatar of Aaron Iker
Aaron Iker

DigitalOcean 为您的每个旅程阶段提供云产品。立即开始使用 $200 免费积分!

如今,完全可以构建自定义复选框、单选按钮和切换开关,同时保持语义和可访问性。我们甚至不需要一行 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,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
83*8083*15.4

移动/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712715.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,它具有相当不错的浏览器支持。

总而言之,这比我们过去不得不应对的开发体验要好得多!