C07 表单控件与原生控件

2026-10-31 17:040 阅读约 17 分钟CSS

C07 表单控件与原生控件

摘要:原生控件由浏览器绘制,直接改颜色与尺寸往往无效。可控的做法分三步:用 appearance: none 移除外观、用 accent-color 着色仍保留原生行为的部分、用自定义元素加 ::before 重建复选框与单选。焦点可见性由 :focus-visible 与 :user-valid 驱动,禁止直接删除 outline。本文给出输入框、复选框、单选、开关与文件上传五类控件的完整样式与四项取舍。
关键词:appearance,accent-color,focus-visible,user-valid,自定义控件

所属:frontend/css / C-高级 | 上一篇:C06-滚动吸附与滚动行为 | 下一篇:C08-图片表格与嵌入内容 | 返回:C00-总结

1 引言

表单是转换率最直接相关的一组控件,也是样式最不容易统一的一组:input 能改内边距,checkbox 改颜色无效,select 的下拉箭头由浏览器绘制,file 的按钮结构不可见。

本文给出三类处理路径与五类控件的完整写法。范围见表 1-1。

项 本文覆盖 本文不覆盖
重置 appearance: none、尺寸与字体继承 浏览器默认样式表差异
着色 accent-color、caret-color、::selection 自定义下拉列表(需 JS)
状态 :focus-visible、:user-valid、:disabled 完整校验(JS 科目 D02)
重建 复选框、单选、开关、文件选择 富文本与日期选择器

表 1-1 本文覆盖范围

2 预备知识

项 要求 核对方式
环境 本地服务器可用 浏览器能打开 localhost
文件 css-practice/forms.html 编码 UTF-8
浏览器 Chrome 130+ 支持 :user-valid
前置 B04、C04 伪类与焦点已掌握

表 2-1 环境与前置要求

3 原理与实现

3.1 三条处理路径

按「能否着色 → 是否重建」选择路径,如图 3-1 所示。

flowchart TD
    A["需要统一样式的控件"] --> B{"原生行为是否可保留"}
    B -->|是,仅换色| C["accent-color 一行解决"]
    B -->|否,需改结构| D{"是否需要单选/复选语义"}
    D -->|是| E["appearance:none + 伪元素重建"]
    D -->|否| F["普通元素 + ARIA(需 JS)"]
    C --> G["保留键盘与读屏支持"]
    E --> G
    F --> H["必须自行补齐键盘与 ARIA"]

图 3-1 三条路径按是否保留原生语义划分

图上可见:前两条路径保留键盘操作与无障碍语义,第三条用 div 模拟控件会把这部分成本转移给脚本,只在确实需要时才走。

3.2 完整实现

<!-- 运行环境:Chrome 130+;css-practice/forms.html -->
<form class="form">
  <label class="field">
    <span>邮箱</span>
    <input type="email" required placeholder="name@example.com" />
    <small>用于接收通知</small>
  </label>

  <label class="check">
    <input type="checkbox" checked />
    <span>接收邮件通知</span>
  </label>

  <fieldset class="choice">
    <legend>配送方式</legend>
    <label class="radio"><input type="radio" name="ship" checked /><span>标准</span></label>
    <label class="radio"><input type="radio" name="ship" /><span>加急</span></label>
  </fieldset>

  <label class="switch">
    <input type="checkbox" role="switch" />
    <span>启用暗色模式</span>
  </label>

  <label class="file">
    <input type="file" accept="image/*" />
    <span class="file__btn">选择图片</span>
  </label>
</form>
/* 运行环境:Chrome 130+;css-practice/forms.css */
:root { --brand: #2563eb; --border: #cbd5e1; --danger: #b91c1c; }

/* 1 基础:控件不继承字体,必须显式声明 */
.form input,
.form select,
.form textarea,
.form button {
  font: inherit;
  color: inherit;
  accent-color: var(--brand);   /* 复选框、单选、range 的着色 */
  caret-color: var(--brand);
}

.form ::selection { background: #bfdbfe; }

.field input {
  inline-size: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
}

/* 2 焦点可见:仅键盘导航时显示轮廓 */
.field input:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-color: var(--brand);
}

/* 3 用户交互后再提示错误,避免一进页面就报红 */
.field input:user-invalid {
  border-color: var(--danger);
}

.field input:user-invalid + small { color: var(--danger); }

/* 4 重建复选框:隐藏原生框,用伪元素绘制 */
.check, .radio, .switch { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }

.check input, .radio input, .switch input {
  appearance: none;
  margin: 0;
  flex: 0 0 auto;
  inline-size: 1.125rem;
  block-size: 1.125rem;
  border: 2px solid var(--border);
  background: #fff;
  display: grid;
  place-content: center;
}

.radio input { border-radius: 50%; }

.check input::before,
.radio input::before,
.switch input::before {
  content: "";
  inline-size: 0.75rem;
  block-size: 0.75rem;
  transform: scale(0);
  transition: transform 120ms ease;
  background: var(--brand);
  clip-path: polygon(14% 44%, 0 58%, 40% 100%, 100% 22%, 86% 8%, 40% 70%);
}

.radio input::before { border-radius: 50%; clip-path: none; }

.check input:checked::before,
.radio input:checked::before { transform: scale(1); }

.check input:checked { border-color: var(--brand); }

/* 5 开关:同一个复选框换个尺寸与圆点 */
.switch input {
  inline-size: 2.75rem;
  block-size: 1.5rem;
  border: none;
  border-radius: 999px;
  background: #cbd5e1;
  justify-content: start;
  padding-inline: 0.125rem;
}

.switch input::before {
  inline-size: 1.25rem;
  block-size: 1.25rem;
  background: #fff;
  border-radius: 50%;
  clip-path: none;
  transform: translateX(0);
}

.switch input:checked { background: var(--brand); }
.switch input:checked::before { transform: translateX(1.25rem); }

/* 6 文件上传:隐藏原生控件,用兄弟元素做按钮 */
.file input { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; }

.file__btn {
  display: inline-block;
  padding: 0.5rem 0.875rem;
  border: 1px dashed var(--border);
  border-radius: 0.5rem;
}

.file input:focus-visible + .file__btn {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

:disabled { opacity: 0.55; cursor: not-allowed; }

控件的三条处理路径见表 3-1。

路径 适用控件 保留原生语义 成本
accent-color 复选框、单选、进度、range 是 一行声明
appearance: none 复选框、单选、开关、文件 是 需伪元素重建
自定义元素 + ARIA 组合框、评分、标签输入 否 需补齐键盘

表 3-1 三条路径的适用范围与成本

状态伪类的差异见表 3-2。

伪类 触发条件 用途
:focus 获得焦点(含鼠标点击) 不用于描边,避免点击后残留
:focus-visible 浏览器判定需要提示时(多为键盘) 焦点轮廓的标准写法
:valid/:invalid 校验状态,页面加载即判定 仅用于无需交互的场景
:user-valid/:user-invalid 用户交互后才判定 表单错误提示的标准写法
:disabled 控件被禁用 降透明度并禁用指针
:placeholder-shown 显示占位文本时 浮动标签动画

表 3-2 六类状态伪类的触发时机差异

4 验证与讨论

  1. 用 Tab 键在表单内移动,确认只有键盘导航时显示蓝色轮廓,鼠标点击输入框不显示。
  2. 在邮箱框输入 abc,确认提交或失焦后才变红,初次加载不报红。
  3. 点击复选框与开关,确认对勾与圆点有过渡动画且状态正确。
  4. 用键盘聚焦隐藏的 file 控件,确认外圈按钮出现焦点轮廓。
检查项 预期结果 说明
字体继承 控件字号与页面一致 font: inherit
焦点轮廓 键盘可见、鼠标点击不残留 表 3-2
延迟报错 未交互时不报红 :user-invalid
状态可见 勾选与开关状态有视觉差异 3.2 节伪元素代码
键盘可达 隐藏的 file 仍可 Tab 到 透明而非 display:none

表 4-1 五项验收

讨论与局限。 appearance: none 之后控件仍由同一个 input 承担语义,读屏与表单提交不受影响;若改用 div 模拟,则需自行实现键盘与 aria-checked。另,:user-invalid 在部分移动端浏览器支持较晚,需配合脚本兜底,见 JS 科目 D02。

5 常见问题

现象 原因 处理
控件字号比页面小 表单控件不继承字体 显式写 font: inherit
改复选框颜色无效 未用 accent-color 或 appearance 二选一,按表 3-1 选路径
点击后轮廓一直存在 用了 :focus 改 :focus-visible
一进页面全部报红 用了 :invalid 改 :user-invalid
隐藏 file 后无法键盘访问 用了 display: none 用透明与 1 像素尺寸隐藏
下拉箭头样式改不掉 select 外观由系统绘制 保留原生或改用自定义列表(需 JS)

表 5-1 表单控件的六类问题

6 结论

序号 结论 依据
1 先判断能否保留原生语义,再选路径 图 3-1
2 控件必须显式声明 font: inherit 3.2 节
3 焦点轮廓用 :focus-visible 表 3-2
4 错误提示用 :user-invalid,不用 :invalid 表 3-2

表 6-1 结论汇总

参考文献

[1] MDN Web Docs · 样式化表单控件,https://developer.mozilla.org/zh-CN/docs/Learn/Forms/Styling_web_forms(访问于 2026-10-31)
[2] MDN Web Docs · accent-color,https://developer.mozilla.org/zh-CN/docs/Web/CSS/accent-color(访问于 2026-10-31)

返回文章列表

评论 0

0/2000

还没有评论,快来抢沙发吧~