<progress> 元素是表示任务完成进度的语义方式,例如下载或上传信息,或完成多步骤流程的一部分。使用原生元素有助于提高无障碍性,因为屏幕阅读器具有隐式 role="progressbar",可以读出值。
本指南实现了一个具有样式自定义功能的完全样式化水平进度条。
进度条还是进度环?
- 如果空间有限或更适合使用方形宽高比,请考虑使用圆形进度条(进度环):进度环
- 对于大多数不确定进度场景,最好使用旋转图标(加载旋转图标)。当不确定状态是暂时的,并且很快会被确定进度条取代时,请使用不确定进度条(例如,在显示进度百分比之前等待上传相关的元数据)
Markup
- 使用原生
<progress>元素:它提供内置无障碍功能和平台一致的行为。 - 如果需要使用不同于默认 0-1 比例的比例,请使用
max。 - 对于特定的进度值,请设置
value属性。对于不确定状态(例如,在开始提供更具体的反馈之前),请省略value。 - 使用
<label for="...">、aria-labelledby或aria-label将进度元素与标签相关联。
需要哪种类型的自定义?
对于有限的样式设置,以下属性就足够了:
accent-color,以更改进度条的颜色width/inline-size可更改进度条的宽度height/block-size适用情况不一致 - 请先测试writing-mode: vertical-lr表示垂直条。- 绘制后应用的效果:
opacity、filter、clip-path、遮罩、转换、box-shadow等。
如果有限的样式设置已足够,请跳过下一部分。
警告:某些属性(例如 border-radius、background、border)会静默停用默认进度条样式。如果您需要上述属性之外的样式,则必须实现完全自定义样式,如下一部分所述。请勿“为了以防万一”而应用更广泛的样式。
完全自定义样式
1. 基本设置
- 强制性:使用
appearance: none启用自定义样式。 - 使用
border: 0 solid重置 UA 边框 - 如需将活动栏剪裁到元素的 border-radius,请使用
overflow: clip或对该栏应用相同的border-radius。请勿在栏上使用border-radius: inherit,这取决于 shadow DOM 中元素的特定结构。
progress {
/* Reset default browser styles */
appearance: none;
border: 0 solid;
/* Clip bar to element’s border-radius */
overflow: clip;
}
2. 样式
- 在
progress元素本身上设置轨道样式。 - 使用适当的浏览器定位伪元素来定位进度条本身:
::-webkit-progress-value用于 WebKit/Blink,::-moz-progress-bar用于 Firefox。 - 强制性要求:请勿在同一规则中将
-webkit-和-moz-伪元素分组。该规则将被视为无效并被忽略。:is()和:where()在这里不起作用,因为它们不支持伪元素。 - 每个浏览器定位的伪元素应只出现一次。特定于状态的自定义(例如不确定状态下的片段大小和动画)应通过在
progress元素本身上设置的自定义属性进行路由。 - 可选:在填充上添加
inline-size过渡效果,使确定值变化时呈现动画效果,而不是直接跳变。Firefox 会在布局中调整填充大小并忽略它,这不会造成任何影响。
progress {
--progress-bar-radius: 1e5px;
border-radius: var(--progress-bar-radius);
/* Apply color for custom progress track (background) */
background: var(--progress-bar-track, var(--color-neutral-90));
/* Subtle depth on the empty part of the track */
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
}
/* Let <progress> styling show through */
progress::-webkit-progress-bar {
background: transparent;
}
/* Webkit, Chrome */
progress::-webkit-progress-value {
background: var(--progress-bar-fill, var(--accent-color));
border-radius: var(--progress-bar-radius);
transition: inline-size 0.3s;
}
/* Firefox */
progress::-moz-progress-bar {
background: var(--progress-bar-fill, var(--accent-color));
border-radius: var(--progress-bar-radius);
transition: inline-size 0.3s;
}
3. 不确定状态样式
为不确定状态添加自定义动画:将填充伪元素转换为短线段,并使其沿轨道滑动。
- 为填充本身设置样式;请勿在
progress上将其重新创建为背景。 - 不确定填充仅通过其动画存在:关键帧使用实际
inline-size设置其长度(这优于 Chromium 的 UA 内嵌inline-size),并使用translate移动它。请勿使用scale作为长度,否则会使填充上绘制的任何图案变形。 translate百分比会根据填充自身的宽度进行解析,因此请改用轨道单位表示位置:将progress设为内嵌大小容器,并在关键帧中使用cqi。在from, to中添加margin-right: auto,以便 Chromium 在 RTL 模式下也将填充内容放置在物理左侧。- 使用转换(而非外边距)来移动填充。
- 转换会将动画保留在合成器中(除非
inline-size也在添加动画效果) - 在 RTL 模式下,外边距会因以下两个不同的原因而失效:
- Firefox 仅使用其物理左外边距来定位不确定填充,因此
margin-inline-start在 RTL 中无效,并且填充永远不会移动。 - 在 Chromium 和 WebKit 中,填充内容会作为普通块进行布局,一旦外边距将填充内容推到轨道末端之外,外边距的总和就会超过轨道。标准 CSS 通过在 RTL 中忽略
margin-left来解决此问题,因此该片段会跳转到边缘,而不是滑出边缘。 translate在布局之后应用,因此这两个问题都不适用。
- 转换会将动画保留在合成器中(除非
- 如需在 RTL 中镜像动作(就像 Chrome 的原生栏一样),请在
:dir(rtl)下反转动画:
progress:dir(rtl) {
--progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
- 在
progress上定义公共--progress-bar-indeterminate-*属性(其名称已将其范围限定为状态),以便使用者无需了解:indeterminate即可替换这些属性。 - 如需改变片段长度(例如,前缘向前移动,后缘跟上),请将
--progress-bar-indeterminate-animation设置为两个并发动画:一个动画的关键帧在cqi(位置)中为translate添加动画效果,另一个动画的关键帧为inline-size(长度)添加动画效果,这样每个动画都有自己的关键帧和加/减速。请勿尝试在单个关键帧列表中同时调整两个边缘;每个关键帧的animation-timing-function会应用于该关键帧中的每个属性。 --progress-bar-animation适用于这两种状态,可用于实现确定性微光等效果。revert-rule仅在设置后才用于替换,以最大限度地提高可组合性。- 切勿停用不确定动画(
animation: none会在 Chromium 中留下零宽度填充)。如需减少动画效果,请改为减慢动画速度。
progress {
/* Lets the fill position itself in track units (cqi) */
container-type: inline-size;
/* Segment length relative to the track */
--progress-bar-indeterminate-size: 30%;
--progress-bar-indeterminate-animation-duration: 1.5s;
/* Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
--progress-bar-indeterminate-animation-duration: 10s;
}
}
progress:indeterminate {
--progress-bar-animation: var(
--progress-bar-indeterminate-animation,
progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear
);
}
/* Add to the existing pseudo-element rules */
progress::-webkit-progress-value {
animation: var(--progress-bar-animation, revert-rule);
}
progress::-moz-progress-bar {
animation: var(--progress-bar-animation, revert-rule);
}
/* Physical: left to right regardless of direction (reverse under :dir(rtl) to mirror). */
@keyframes progress-bar-indeterminate {
from, to {
inline-size: var(--progress-bar-indeterminate-size);
/* Pin to the physical left; Chromium would otherwise lay the fill out at the inline start */
margin-right: auto;
}
from { translate: -100%; } /* Own width: fully outside the left edge */
to { translate: 100cqi; } /* Track width: fully outside the right edge */
}
其他无障碍注意事项
除了上述准则之外,还应遵循以下准则:
- 在不抢占焦点的情况下宣布完成:请勿在
<progress>上设置tabindex或调用.focus(),因为它是非交互式的,将焦点移至它会干扰键盘用户。如需在后台任务完成时发出通知,请更新礼貌状态元素(role="status"或aria-live="polite")。 - 请勿在
<progress>内添加后备文本:现代浏览器会忽略<progress>内的文本,辅助技术也不会使用这些文本。 - 避免在未加标签的通用
<div>上使用aria-busy+aria-describedby。aria-busy具有不一致的屏幕阅读器支持,并且辅助技术会忽略未命名<div>(没有角色)上的aria-describedby。如果某个区域的内容在加载时未准备就绪,请使用hidden或aria-hidden="true"将其隐藏。 - 务必确保进度条和轨道之间有适当的对比度,尤其是在添加自定义样式时。
- 考虑使用其他方式来表示不确定状态:与其他内容并行显示的不确定进度条动画可能会让某些用户分心。
后备策略
accent-color 的回退和浏览器支持
accent-color 属性是渐进增强。
不支持此属性的浏览器会忽略它,并使用其默认界面颜色。
如果不支持 accent-color,并且需要为进度条设置样式,请使用自定义样式设置方法为进度条应用首选样式。
revert-rule 的后备方案和浏览器支持
如果 revert-rule 不受支持,则在未设置 --progress-bar-animation 时,animation: var(--progress-bar-animation, revert-rule) 会回退到 animation-name: revert-rule,时长为 0s,在不存在 @keyframes revert-rule 时,这是一种无害的空操作。