构建无障碍组件之Meter Pattern
Meter Pattern 详解:构建无障碍计量器组件
Meter(计量器)是一种图形化显示数值的组件,用于展示在特定范围内变化的数值。本文基于 W3C WAI-ARIA Meter Pattern 规范,详解如何构建无障碍的 Meter 组件。
一、Meter 的定义与核心概念
1.1 什么是 Meter
Meter 是一种图形化显示数值的组件,具有以下特征:
- 显示一个在定义范围内变化的数值
- 通常以视觉形式呈现(如进度条、仪表盘、电池图标等)
- 数值有明确的最小值和最大值限制
1.2 Meter vs Progressbar
Meter 和 Progressbar 容易混淆,但它们有明确的区别:
| 特性 | Meter | Progressbar |
|---|---|---|
| 用途 | 显示当前状态值(如电量、油量) | 显示任务进度(如加载中、完成百分比) |
| 数值变化 | 随时间自然变化 | 随任务推进单向增长 |
| 典型场景 | 电池电量、磁盘使用率、温度 | 文件上传、表单提交、安装进度 |
重要提示:
- Meter 不应用于表示进度(如加载或任务完成百分比)
- Meter 不适用于没有明确最大值的情况(如世界人口数量)
1.3 核心术语
| 术语 | 说明 |
|---|---|
| Value | 计量器当前显示的数值 |
| Minimum Value | 计量器的最小值(aria-valuemin) |
| Maximum Value | 计量器的最大值(aria-valuemax) |
| Current Value | 当前值(aria-valuenow) |
┌─────────────────────────────────────────────────────────────┐
│ Meter Container │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │████████████████████████████████░░░░░░░░░░░░░░░░░░░░░│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 0% 25% 50% 75% 100% │
│ ↑ ↑ │
│ Minimum Maximum │
│(aria-valuemin) (aria-valuemax) │
│ │
│ Current: 60% │
│ (aria-valuenow) │
└─────────────────────────────────────────────────────────────┘
二、HTML <meter> 标签 vs ARIA role="meter"
在实现 Meter 组件时,我们有两种选择:原生 HTML <meter> 标签 或 ARIA role="meter"。
2.1 两种方式对比
| 特性 | HTML <meter>
|
ARIA role="meter"
|
|---|---|---|
| 本质 | 原生 HTML5 语义化标签 | ARIA 角色属性 |
| 浏览器支持 | 现代浏览器原生支持 | 所有支持 ARIA 的浏览器 |
| 可定制性 | 样式受限(浏览器控制) | 完全可定制 |
| 代码简洁度 | 简洁,内置语义 | 需要显式声明 ARIA 属性 |
| 辅助技术识别 | 自动识别为 meter | 通过 role 识别为 meter |
2.2 使用 HTML <meter> 标签(推荐)
HTML5 提供了原生的 <meter> 标签,它自动具有 role="meter" 的语义,无需额外声明:
<meter value="60" min="0" max="100">60%</meter>
<meter> 标签的属性:
| 属性 | 说明 | 示例值 |
|---|---|---|
value |
当前值 | "60" |
min |
最小值 | "0" |
max |
最大值 | "100" |
low |
低值阈值 | "25" |
high |
高值阈值 | "75" |
optimum |
最佳值 | "90" |
示例:带颜色区间的电池电量
<meter
value="45"
min="0"
max="100"
low="20"
high="80"
optimum="90"
aria-label="电池电量">
45%
</meter>
-
low以下:浏览器通常显示为红色(危险) -
low到high之间:黄色(警告) -
high以上:绿色(正常)
2.3 使用 ARIA role="meter"
当需要完全自定义样式(如电池图标、仪表盘、信号格等)时,使用 ARIA 方式:
<div role="meter" aria-label="电池电量" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100">
<!-- 自定义视觉表现 -->
</div>
2.4 如何选择?
优先使用 <meter> 标签:
- 简单的进度条场景
- 不需要复杂自定义样式
- 追求代码简洁性
使用 ARIA role="meter":
- 需要自定义视觉样式(电池图标、仪表盘等)
- 特殊形状或动画效果
- 需要兼容旧浏览器
2.5 两种方式的等价关系
以下两种实现在辅助技术眼中是等价的:
<!-- 方式1:HTML 原生标签 -->
<meter value="60" min="0" max="100" aria-label="电池电量">60%</meter>
<!-- 方式2:ARIA 实现 -->
<div role="meter" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" aria-label="电池电量">
60%
</div>
注意:<meter> 标签已经内置了 role="meter" 语义,不需要额外添加 role 属性。
三、WAI-ARIA 角色与属性(ARIA 方式)
当使用 <meter> 标签时,以下 ARIA 属性自动处理,无需手动声明:
| HTML 属性 | 对应的 ARIA 属性 | 说明 |
|---|---|---|
value |
aria-valuenow |
自动映射 |
min |
aria-valuemin |
自动映射 |
max |
aria-valuemax |
自动映射 |
当使用 role="meter" 时,需要手动声明以下属性:
3.1 必需属性
Meter 组件需要以下 ARIA 属性:
| 属性 | 说明 | 示例值 |
|---|---|---|
aria-valuenow |
当前值(必须在 min 和 max 之间) | "60" |
aria-valuemin |
最小值 | "0" |
aria-valuemax |
最大值 | "100" |
aria-label 或 aria-labelledby
|
计量器的可访问标签 | "电池电量" |
<div
role="meter"
aria-label="电池电量"
aria-valuenow="60"
aria-valuemin="0"
aria-valuemax="100">
<!-- 计量器视觉表现 -->
</div>
3.2 可选属性
aria-valuetext
当仅显示百分比不够友好时,使用 aria-valuetext 提供更友好的值描述:
<div
role="meter"
aria-label="电池电量"
aria-valuenow="50"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="50% (6小时) 剩余">
<!-- 计量器视觉表现 -->
</div>
辅助技术会读取 aria-valuetext 而不是简单的百分比数值。
3.3 属性关系
aria-valuemin < aria-valuenow < aria-valuemax
↑ ↑ ↑
最小值 当前值 最大值
0 60 100
约束条件:
-
aria-valuemin必须小于aria-valuemax -
aria-valuenow必须在aria-valuemin和aria-valuemax之间 - 所有值都使用十进制数值
三、键盘交互规范
Meter 组件没有特定的键盘交互,因为它通常是一个只读组件,用户不能直接操作它。
如果 Meter 是可交互的(如可调节的范围选择器),应该使用 role="slider" 而不是 role="meter"。
四、实现方式
4.1 基础 Meter 结构
<div
class="meter"
role="meter"
aria-label="电池电量"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100">
<div class="meter-bar">
<div
class="meter-fill"
style="width: 75%;">
</div>
</div>
<span class="meter-value">75%</span>
</div>
4.2 使用 aria-valuetext 的示例
<div
class="meter"
role="meter"
aria-label="剩余存储空间"
aria-valuenow="45.5"
aria-valuemin="0"
aria-valuemax="128"
aria-valuetext="45.5 GB 已使用,共 128 GB">
<div class="meter-bar">
<div
class="meter-fill"
style="width: 35.5%;">
</div>
</div>
<span class="meter-value">45.5 GB / 128 GB</span>
</div>
4.3 带颜色状态的 Meter
根据数值范围显示不同颜色(如危险、警告、正常):
<div
class="meter meter-danger"
role="meter"
aria-label="CPU 使用率"
aria-valuenow="95"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="95%,危险">
<div class="meter-bar">
<div
class="meter-fill"
style="width: 95%;">
</div>
</div>
<span class="meter-value">95%</span>
</div>
五、常见应用场景
5.1 电池电量显示
<div
role="meter"
aria-label="电池电量"
aria-valuenow="45"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="45% (约3小时) 剩余">
<!-- 电池图标:外框 + 电量填充 -->
<div class="battery-icon">
<div class="battery-body">
<div class="battery-level" style="width: 45%;"></div>
</div>
<div class="battery-cap"></div>
</div>
<span>45%</span>
</div>
5.2 磁盘使用率
<div
role="meter"
aria-label="磁盘使用率"
aria-valuenow="72"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="72% 已使用 (360 GB / 500 GB)">
<div class="meter-bar">
<div style="width: 72%"></div>
</div>
<span>72% 已使用</span>
</div>
5.3 温度显示
<div
role="meter"
aria-label="CPU 温度"
aria-valuenow="65"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="65°C">
<div class="meter-bar">
<div style="width: 65%"></div>
</div>
<span>65°C</span>
</div>
5.4 信号强度
<div
role="meter"
aria-label="WiFi 信号强度"
aria-valuenow="3"
aria-valuemin="0"
aria-valuemax="4"
aria-valuetext="3格信号 (良好)">
<div class="signal-bars">
<span class="bar active"></span>
<span class="bar active"></span>
<span class="bar active"></span>
<span class="bar"></span>
</div>
</div>
六、最佳实践
6.1 正确选择使用场景
使用 Meter:
- 电池电量
- 磁盘/存储使用率
- 温度、压力等物理量
- 信号强度
- 任何有明确范围的数值
不使用 Meter(改用 Progressbar):
- 文件上传进度
- 安装进度
- 任务完成百分比
- 任何表示"进度"的场景
不使用 Meter(改用其他组件):
- 世界人口(无最大值)
- 可调节的数值(使用 Slider)
6.2 提供清晰的标签
始终为 Meter 提供描述性的标签:
<!-- 好的示例 -->
<div role="meter" aria-label="电池电量">...</div>
<!-- 不好的示例 -->
<div role="meter">...</div>
6.3 使用 aria-valuetext 增强可读性
当纯百分比不够直观时,使用 aria-valuetext:
<!-- 好的示例 -->
<div
role="meter"
aria-label="电池"
aria-valuenow="50"
aria-valuetext="50% (6小时) 剩余">
...
</div>
<!-- 不好的示例 -->
<div
role="meter"
aria-label="电池"
aria-valuenow="50">
...
</div>
6.4 确保数值在有效范围内
// 确保 aria-valuenow 在有效范围内
function updateMeter(element, value) {
const min = parseFloat(element.getAttribute('aria-valuemin'));
const max = parseFloat(element.getAttribute('aria-valuemax'));
// 限制值在范围内
const clampedValue = Math.max(min, Math.min(max, value));
element.setAttribute('aria-valuenow', clampedValue);
}
6.5 视觉与 ARIA 值保持一致
确保视觉表现和 ARIA 属性值同步更新:
function setMeterValue(element, value) {
const min = parseFloat(element.getAttribute('aria-valuemin'));
const max = parseFloat(element.getAttribute('aria-valuemax'));
// 更新 ARIA 值
element.setAttribute('aria-valuenow', value);
// 更新视觉表现
const percentage = ((value - min) / (max - min)) * 100;
const fillElement = element.querySelector('.meter-fill');
fillElement.style.width = percentage + '%';
// 更新文本
const valueElement = element.querySelector('.meter-value');
valueElement.textContent = Math.round(percentage) + '%';
}
6.6 考虑颜色对比度
确保 Meter 的不同状态颜色具有足够的对比度:
.meter-fill {
background-color: #3b82f6; /* 蓝色 - 正常 */
}
.meter-warning .meter-fill {
background-color: #f59e0b; /* 黄色 - 警告 */
}
.meter-danger .meter-fill {
background-color: #ef4444; /* 红色 - 危险 */
}
七、总结
Meter 组件虽然简单,但正确使用 ARIA 属性对于无障碍体验至关重要:
-
使用正确的 role:
role="meter"用于显示范围内的数值 -
设置必需的属性:
aria-valuenow、aria-valuemin、aria-valuemax -
提供清晰的标签:使用
aria-label或aria-labelledby -
增强可读性:使用
aria-valuetext提供更友好的值描述 - 区分使用场景:Meter vs Progressbar vs Slider
遵循 W3C Meter Pattern 规范,我们能够创建既美观又无障碍的计量器组件,为所有用户提供清晰的状态信息。
文章同步于 an-Onion 的 Github。码字不易,欢迎点赞。