1. 简介
本节是非规范性的。
用户代理长期以来一直提供非标准的方式来设置表单控件的样式。 然而,所有这些控件在各用户代理中的实现都不一致, 给作者带来了不必要的摩擦。
本模块旨在足够详细地定义一组表单控件部件, 使它们能够以可互操作的方式使用。
它还定义了一些自定义表单控件的新方式, 涵盖了一些常见用例,这些用例以前只能通过 从头实现自定义控件来实现,而这需要大量工作、 难以正确实现,并且常常会破坏可访问性或平台约定。
2. 选择启用基本外观:appearance: base 值
| 名称: | appearance |
|---|---|
| 新值: | base |
当应用于表单控件时,base 会将该控件置于基本外观状态。
具有基本 外观的控件,可以使用标准 CSS 以及下文定义的伪元素以一致的方式设置样式,并且 应用可覆盖的默认样式,这些默认样式在各 UA 中保持一致。 当控件处于该状态时,用户代理会将附录 A:基本外观用户代理样式表中的样式应用于该 控件。
用户代理还必须启用§ 4 伪元素定义的伪元素。这些伪元素(不包括 ::picker())始终 从其源元素继承 appearance。用户代理可以使用 appearance: inherit !important 声明来实现这一点。
注: 这种继承会防止作者在同一个控件中混用 原生部件和非原生部件。
2.1. 基本外观的设计原则
以下设计原则适用于表单控件基本外观样式表的设计, 按重要性递减顺序排列:
-
这些样式在每个用户代理中都是相同的。
-
控件在没有额外样式的情况下也能被识别并可用。
-
控件通过 100% 的 WCAG 2.2 AA 标准。
-
这些样式在各控件之间保持一致……
-
……在外观与感觉方面。
-
……在代码定义方式方面。
-
……在尺寸和交互方面。
-
-
这些样式无需复杂的重置样式表,就能轻松适配网站的品牌风格:
-
它们使用最少的代码,并且易于覆盖。
-
它们本身没有强烈的风格与语气,并且在视觉上尽可能简单。
-
只要可能,它们就继承页面样式,而不是定义新样式。
-
它们对调整具有韧性……
-
……当自身被更改时(例如更改字体、边框、布局)。
-
……当置于上下文中时(例如准备作为 flex 或 grid 子项)。
-
-
-
它们是全面的:
-
覆盖每个控件的所有状态。
-
支持所有书写模式和配色方案。
-
对于 HTML 表单控件而言,这些原则专门通过附录 A:基本外观用户代理样式表中 定义的必需用户代理样式表来应用。
2.2. 示例
通过实现、实验、 bikeshedding 以及对用户代理样式表的改进来完善这些示例。
这些示例的主要目的是展示基本外观的设计原则如何在实践中应用。
要在单个控件上应用基本 外观,可使用以下代码:
input, textarea, meter, progress, button, select{ appearance : base; }
注: 以下示例使用的表单布局 未作详细说明。
2.2.1. 默认用户代理颜色
下面是分别从根元素继承默认浅色和深色模式颜色的基本 外观颜色:

2.2.2. 颜色/字体自定义
下面是在基本外观之上进行自定义的一些示例:
form{ font-family : "American Typewriter" ; background-color : rgb ( 254 , 252 , 221 ); color : rgb ( 131 , 17 , 0 ); } input, textarea, meter, progress, button, select{ appearance : base; }

form{ font-family : "Courier New" ; font-size : 14 px ; background-color : rgb ( 0 , 0 , 0 ); color : rgb ( 0 , 249 , 0 ); } input, textarea, meter, progress, button, select{ appearance : base; }

3. 设置选择器样式
3.1. ::picker() 伪元素
::picker() 伪元素表示表单控件中从页面弹出的部分。
::picker() = ::picker( <form-control-identifier>+ ) <form-control-identifier> = select
::picker() 伪元素只在源元素支持基本外观并具有弹出式选择器时匹配。指定的
<form-control-identifier> 还必须匹配
源元素的唯一选择器
名称。例如,
select
元素的唯一选择器名称
是 select。
为了让 ::picker() 伪元素被渲染,它及其源元素都必须具有计算后的 appearance 值 base。
select, select::picker ( select) { appearance : base; } select::picker ( select) { border : 5 px solid red; background-color : blue; }
注: ::picker() 的非函数形式目前无法防止随着新选择器受支持而意外设置选择器样式。 一旦所有表单控件选择器的样式设置最终确定,此非函数形式将适用于所有选择器。
4. 伪元素
表单控件由许多部分组成,作者可能希望分别设置这些部分的样式, 因此需要用户代理为各个表单控件提供伪元素。
以下小节介绍了一组伪元素,试图覆盖最常见的用例, 以便它们能够在各用户代理之间以一致的方式被处理。
| 控件 | 伪元素 |
|---|---|
<progress>
|
├─ ''::track'' │ └─ ''::fill'' └─ ''::thumb'' |
<meter>
| |
<input type=checkbox switch>
| |
<input type=range>
| |
<input type=checkbox>
| ::checkmark |
<input type=radio>
| |
<input type=file>
| ::file-selector-button |
<input type=date>
|
见 § 4.8 设置日期/时间输入字段各部分样式: ::field-component 和 ::field-separator 伪元素 |
<input type=datetime-local>
| |
<input type=time>
| |
<input type=month>
| |
<input type=week>
| |
<input>(无 type)
| 见 § 4.5 设置文本字段各部分样式:::field-text 和 ::clear-icon 伪元素 |
<input type=text>
| |
<input type=search>
| |
<input type=email>
| |
<input type=password>
| |
<input type=tel>
| |
<input type=url>
| |
<input type=number>
| 见 § 4.7 设置数字字段各部分样式:::step-control、 ::step-up 和 ::step-down 伪元素 |
<input type=color>
| ::color-swatch |
<textarea>
| 见 § 4.6 设置 textarea 各部分样式:::placeholder 和 ::field-text 伪元素 |
<select>
| ::picker-icon |
<option>
| ::checkmark |
| 按钮 |
4.1. 选择器打开图标:::picker-icon 伪元素
::picker-icon 伪元素表示控件中代表选择器存在的图标部分。 它只在源元素具有基本外观并且会打开选择器时生成。 它是一个完全可设置样式的伪元素,并从其源元素继承。
::picker-icon 生成一个盒子,就好像它是其源元素的子项一样,位于 ::after 伪元素生成的任何盒子之后,其内容由 content 指定。
4.2. 文件选择器按钮:::file-selector-button 伪元素
::file-selector-button 伪元素表示用于打开文件选择器的按钮, 如果 UA 渲染了这样的按钮。
它通常定位于
button
内部,即
input
元素中 type=file 的那个按钮。
它是一个由元素支持的伪元素。
4.3. 设置勾选标记样式:::checkmark 伪元素
::checkmark 伪元素表示某项是否被选中的指示器, 并存在于复选框、单选按钮和 option 元素上。
它只在源元素支持 :checked 伪类,并且其自身具有基本外观或祖先具有基本外观时生成。 它是一个完全可设置样式的伪元素,并从其源元素继承。
对于复选框和单选按钮元素,它生成一个盒子,就好像它是其源元素的子项一样,位于 ::before 和 ::after 伪元素生成的盒子之间,其内容由 content 指定。
对于 option 元素,它生成一个盒子,就好像它是其源 元素的子项一样,位于 ::before 伪元素生成的任何盒子之前,其内容由 content 指定。
::checkmark{ background-image : url ( ... ) }
它也可以与 :indeterminate 结合使用,以设置不确定状态勾选标记的样式:
:indeterminate::checkmark{ background-image : url ( ... ) }
4.4. 设置类滑块控件各部分样式:::thumb、::track 和 ::fill 伪元素
命名仍在 讨论中。[Issue #9830]
类滑块 控件是表示进度的表单控件。 该进度可以由用户调整。
提供以下伪元素来设置其不同部分的样式:
- ::thumb
-
::thumb 伪元素表示
允许用户调整控件进度的部分。
注: 在大多数用户代理中,它通常会被原生渲染为 圆形。
- ::track
- ::track 伪元素表示包含控件中 已进展部分和未进展部分的部分。
- ::fill
-
::fill
伪元素表示
包含控件中已进展部分的部分。
当控件的进度不确定时(例如 <progress indeterminate>), 用户代理必须使此部分的 inline-size 为零。
这些伪元素是完全 可设置样式的伪元素,其结构如下:
<input type="range"> ├─ ::track │ └─ ::fill └─ ::thumb
类滑块控件的列表取决于宿主语言。对于 HTML,这 对应于:
4.5. 设置文本字段各部分样式:::field-text 和 ::clear-icon 伪元素
- ::placeholder
- ::placeholder 伪元素表示
input中包含占位文本的部分。 - ::field-text
- ::field-text 伪元素表示
input中包含可编辑文本的部分。 - ::clear-icon
-
::clear-icon 伪元素表示
input中允许用户在点击时清除input的部分,如果用户代理提供了该部分。使用 appearance: textfield 时,用户代理不得生成 此部分。
::field-text 和 ::clear-icon 必须是兄弟。
是否为实现密码可见性切换的 用户代理定义某些内容?[Issue #11845]
定义
::placeholder 如何与 ::field-text 交互。[Issue #11844]
4.6. 设置 textarea 各部分样式:::placeholder 和 ::field-text 伪元素
- ::placeholder
- ::placeholder 伪元素表示
textarea中包含占位文本的部分。 - ::field-text
- ::field-text 伪元素表示
textarea中包含可编辑文本的部分。
为 调整大小控件定义某些内容。[Issue #11850]
定义
::placeholder 如何与 ::field-text 交互。[Issue #11844]
4.7. 设置数字字段各部分样式:::step-control、::step-up 和 ::step-down 伪元素
这些伪元素为数字输入提供。它们是完全 可设置样式的伪元素。
- ::step-control
- ::step-control 伪元素表示 数字输入中包含向上和向下按钮的部分。
- ::step-up
- ::step-up 伪元素表示 激活时会递增数字输入内部值的按钮。
- ::step-down
- ::step-down 伪元素表示 激活时会递减数字输入内部值的按钮。
它们的结构定义如下:
<input type="number"> ├─ ::field-text └─ ::step-control ├─ ::step-up └─ ::step-down
< input type = "number" >
可以像这样重新设置样式:
[ + 2 - ]
使用以下样式:
input[ type=number] { appearance : base; &::step-control{ display : contents; } &::step-up{ order : 1 ; content : "+" ; } &::field-text{ order : 2 ; } &::step-down{ order : 3 ; content : "-" ; } }
使用 appearance: textfield 时,用户代理不得生成此部分。
4.8. 设置日期/时间输入字段各部分样式:::field-component 和 ::field-separator 伪元素
- ::field-component
- ::field-component 伪元素表示 控件中包含日期/时间组成部分值的各部分。
- ::field-separator
- ::field-separator 伪元素表示 控件中分隔日期/时间组成部分值的各部分,如果用户代理提供了这些部分。
这些伪元素是兄弟。控件的确切结构由国际化 和宿主语言决定, 但必须在各用户代理之间保持一致。
< input type = "date" >
在美国 locale 中可能渲染如下:
[ 08 / 22 / 2024 [v]]
得到的树为:
<input type="date"> ├─ ::field-component (08) ├─ ::field-separator (/) ├─ ::field-component (22) ├─ ::field-separator (/) ├─ ::field-component (2024) └─ ::picker-icon
4.9. 颜色样本:::color-swatch 伪元素
名称应该是 ::swatch 还是 ::color-swatch?[Issue #11837]
::color-swatch 伪元素表示控件中显示所选颜色值的部分。
4.10. 与厂商伪元素扩展的兼容性
在可能的情况下,用户代理应使用别名来实现任何非标准伪元素。
在不可能的情况下,用户代理必须将标准伪元素保留给 appearance: base,并将任何非标准伪元素用于 appearance: none。
5. 伪类
5.1. 定位不同的 meter 状态::low-value / :high-value / :optimal-value 伪类
确保这能够 复制 UA 逻辑。[Issue #11336]
:low-value
伪类在
meter
元素的值低于 low HTML 属性所指定的值时匹配。
:high-value 伪类在
meter
元素的值高于 high HTML 属性所指定的值时匹配。
:optimal-value 伪类在
meter
元素的值处于由 optimum / low /
high HTML 属性确定的范围内时匹配。
5.2. 定位作为列表框的 select
定义某些内容。[Issue #7422]
6. control-value() 函数
考虑与数据外泄相关的 隐私影响。[Issue #11860]
考虑添加更多 类型。[Issue #11842]
control-value() 函数计算为其所在表单控件的当前值。 如果它用于不是表单控件的元素, 则返回空字符串。
<control-value()> = control-value( <type>? ) <type> = '<' [ number | string ] '>'
如果用于伪元素,则会针对其源元素求值。
7. 设置部件样式
是否将 field-sizing/accent-color/input-security 移入此规范?
7.1. 更改类滑块控件的方向:slider-orientation
| 名称: | slider-orientation |
|---|---|
| 值: | auto | left-to-right | right-to-left | top-to-bottom | bottom-to-top |
| 初始值: | auto |
| 适用于: | 所有元素 |
| 继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定值 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
- auto
- 类滑块 控件的方向由书写模式和方向定义。
- left-to-right
- 类滑块 控件水平渲染,并且 ::fill 在控件内左对齐。
- right-to-left
- 类滑块 控件水平渲染,并且 ::fill 在控件内右对齐。
- top-to-bottom
- 类滑块 控件垂直渲染,并且 ::fill 在控件内顶部对齐。
- bottom-to-top
- 类滑块 控件垂直渲染,并且 ::fill 在控件内底部对齐。
附录 A:基本外观用户代理样式表
color input 样式需要 完善。[Issue #11837]
基础
input, textarea, button, ::file-selector-button, select, meter, progress{ color : inherit; font : inherit; box-sizing : border-box; background-color : transparent; }
布局
input : not ([ type=file], [ type=range]), textarea, button, ::file-selector-button, ::track, select, meter, progress{ border : 1 px solid currentColor; background-color : transparent; }
滑块
完善 meter、progress、 switch 和 range input 的样式。
::track{ height : 1 em ; } ::fill{ height : 100 % ; background-color : currentColor; } ::thumb{ border-radius : 0 ; border : none; background-color : currentColor; appearance : none; width : 1 em ; height : 100 % ; }
复选框和 单选按钮
input : is ([ type=checkbox] :not ([ switch]), [ type=radio]) { width : 1 em ; height : 1 em ; display : inline-flex; align-items : center; justify-content : center; content : '' ; } input[ type=radio] { border-radius : 100 % ; } input[ type=checkbox] :not ([ switch]) :checked::checkmark{ content : '\2713' /'' ; } input[ type=radio] :checked::checkmark{ background-color : currentColor; display : inline-block; border-radius : inherit; height : 100 % ; width : 100 % ; }
Select 和按钮
select{ /* Base appearance select always sizes based on its contents. */ field-sizing: content !important; } button, ::file-selector-button, select, input:is ([ type="color" ], [ type="button" ], [ type="reset" ], [ type="submit" ]) { border : 1 px solid currentColor; background-color : transparent; color : inherit; /* Padding prevents the text from sticking to the borders. * optically centered to account for half leading */ padding-block:0.25 em ; padding-inline : 0.5 em ; /* <select>s and <button>s should have border-radius to be * distinct from <input>s: https://github.com/w3c/csswg-drafts/issues/10857#issuecomment-2520875011*/ border-radius:0.5 em ; /* These min-size rules ensure accessibility by following WCAG rules: * https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html * The 1.2em is there to make sure that options without text don't change * the block size of the button. */ min-block-size:max ( 24 px , 1 lh ); min-inline-size : 24 px ; /* box-sizing comes from existing UA styles which happen to * already be interoperable. */ box-sizing: border-box; /* Push picker icon to the right of the box and have some space * in between it and the text. */ display: inline-flex; gap : 1 em ; user-select : none; } :is ( button, select, input:is ([ type="color" ], [ type="button" ], [ type="reset" ], [ type="submit" ])) :enabled:hover, :enabled::file-selector-button:hover{ background-color : color-mix ( in lab, currentColor10 % , transparent); } :is ( button, select, input:is ([ type="color" ], [ type="button" ], [ type="reset" ], [ type="submit" ])) :enabled:active, :enabled::file-selector-button:active{ background-color : color-mix ( in lab, currentColor20 % , transparent); } :is ( button, select, input:is ([ type="color" ], [ type="button" ], [ type="reset" ], [ type="submit" ])) :disabled, :disabled::file-selector-button{ color : color-mix ( in srgb, currentColor50 % , transparent); } select > button:first-child{ /* Prevents button from setting font, color, or background-color */ all: unset; /* Prevents duplicate box decorations */ display: contents; /* Prevents button activation behavior so select can handle events */ interactivity: inert; } select option{ /* These min-size rules ensure accessibility by following WCAG rules: * https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html * Unset if the author provides a child button element. * The 1lh is there to make sure that options without text don't change * the block size of the option. */ min-inline-size:24 px ; min-block-size : max ( 24 px , 1 lh ); /* Centers text within the block (vertically). From OpenUI discussion: * https://github.com/openui/open-ui/issues/1026#issuecomment-2103187647. */ align-content: center; /* centering + gap between checkmark and option content */ /* also easily reversed, when checkmark should be inline-end */ display: flex; align-items : center; gap : 0.5 em ; /* Makes options with long text widen picker instead * of making options tall. */ white-space: nowrap; } select option:enabled:hover{ background-color : color-mix ( in lab, currentColor10 % , transparent); } select option:enabled:active{ background-color : color-mix ( in lab, currentColor20 % , transparent); } select option:disabled{ color : color-mix ( in lab, currentColor50 % , transparent); } select option::checkmark{ content : '\2713' /'' ; } select option:not ( :checked) ::checkmark{ visibility : hidden; } select optgroup{ /* font-weight makes optgroups visually distinct from options. */ font-weight: bolder; } select optgroup option{ /* Undo font-weight:bolder rule from optgroups. */ font-weight: normal; } select legend, select option{ /* spacing ownership moves to children */ /* space inline from border edges */ /* this creates a full bleed hover highlight */ padding-inline:0.5 em ; } select::picker-icon{ /* margin-inline-start pushes the icon to the right of the box */ margin-inline-start: auto; display : block; content : counter ( -ua-disclosure-open, disclosure-open); } ::picker ( select) { /* Same properties as popover and dialog */ color: CanvasText; background-color : Canvas; border : 1 px solid; /* box-sizing is set to match the button. */ box-sizing: border-box; /* Remove [popover] padding which * prevents options from extending to edges */ padding:0 ; /* Anchor positioning and scrollbars */ inset: auto; margin : 0 ; min-inline-size : anchor-size ( self-inline); min-block-size : 1 lh ; /* Go to the edge of the viewport, and add scrollbars if needed. */ max-block-size: stretch; overflow : auto; /* Below and span-right, by default. */ position-area: block-end span-inline-end; position-try-order : most-block-size; position-try-fallbacks : /* First try above and span-right. */ block-start span-inline-end, /* Then below but span-left. */ block-end span-inline-start, /* Then above and span-left. */ block-start span-inline-start; }
附录 B: 探索
基本样式提案
本节概述了一些用于解决表单样式问题的提案。
原型
这个想法最初由 fantasai 提出, 即我们可以为少数“原型”元素设置样式。 浏览器 UI 设计者随后可以获取这些元素的样式, 并将该设计推导到他们自己的 UI 中。 至少可以使用文本、背景和边框等内容。 在极限情况下,内部 padding、border-radius 等内容也可能被使用。
@control button{ <declaration-list>} @control input{ <declaration-list>} input::selection{ <declaration-list>} ...
你将能够对以下内容使用样式:
字体选择(css-fonts)
文本装饰(css-text-decor)
文本布局(css-text)
背景和投影(css-backgrounds)
边框和 padding(css-backgrounds)
滤镜
我们或 UA 认为相关的任何其他内容
大多数表单控件,甚至日历部件或时钟, 在某种程度上都是这三种基本元素的组合。如果 UA 获得了 这三种基本元素的样式,或许再加上一两个元素,它就可以弄清楚如何 设置其余部分的样式。
例如,日历部件可能包含月份、年份、一些用于切换它们的按钮、 可以点击进入并直接编辑它们的能力, 以及对当月日期的呈现。选中的日期是被选中的。 也许按钮只在 :hover 或 :focus 时显示——这由 UA 决定。但 它知道按钮应当是这种特定的蓝色、带有那种 特定的 border-radius 和 drop-shadow。日历可能以输入字段的颜色显示, 选中日期则使用选择颜色, 并且在所有方面都将与页面其余部分中输入字段的外观相匹配。
现在,作者不能决定,例如,年份和月份名称之间的间距, 或者用于更改年份的按钮是实心箭头、空心箭头还是花哨的箭头, 也不能决定月份和下方日期字段之间 应该有一条 5px 间距的黑色实心半边框。但日历会看起来属于 该页面,并且当 UA 在宽而矮的智能手表上发布一个不同的日历布局时, 它可以将月份和年份更好地放在侧边, 而不会破坏任何内容。
反向系统颜色
这个想法最初由 Florian 和 Tab 勾勒, 即定义一组抽象颜色, 让 UI 设计者在为其 UI 着色时可以从中选择。
Tab 建议的颜色集,来自一个 Android 颜色提取 API 提案:
-
浅色/普通/深色鲜艳色
-
浅色/普通/深色柔和色
-
鲜艳互补色(用于需要在视觉上显著区分的强调按钮等)
(其中浅色约为 75% 亮度,普通约为 50%,深色约为 25%;鲜艳色 至少为 30% 饱和度,理想情况下为 100%,而柔和色最多为 40% 饱和度,理想情况下为 30%)
-
浅色/深色对比文本
-
浅色/深色对比次要文本
这是 11 种颜色,其中许多应该可以从网页自身的配色方案中绘制出来。 如果你至少指定其中一些, 我们可以自动生成一批颜色, 例如从 "normal-vibrant" 颜色生成浅色/深色变体, 或者自动将文本颜色设置为适当的白色/黑色。
不过,不能保证输入 UI 会以与页面其余部分相同的方式使用这些颜色。
其他控件特有的建议
插入 <select> 和 <datalist> 样式提案和/或白板照片。
Select/Datalist 下拉菜单
-
只有在同时设置 color 和 background 时才允许设置样式。
-
Option 容器:
-
背景
-
边框
-
padding
-
-
-
padding
-
边框
-
border-collapse?
-
背景
-
允许 display-inside,并自动 blockified
-
不包括 margin、position、float、width、height
-
所有 option 都是 contain:paint 和 BFC。
输入 UI 示例
本节会尽可能多地汇集我们能够截图的输入 UI 示例, 尤其是在移动设备上,它们会有些“奇怪”。
时间选择器
日期选择器
隐私考量
尚未报告此规范带来新的隐私考量。
安全考量
尚未报告此规范带来新的安全考量。
致谢
感谢 Aditya Keerthi、Anne van Kesteren、Elika Etemad、Jen Simmons、Joey Arhar、Jon Davis、Simon Fraser 和 Theresa O’Connor 对此提案提供的意见。
感谢 Ana Tudor 对 input[type=range] 样式所做的详细
分析。