应优先使用语义匹配的原生控件:二元状态用 <input type="checkbox">,单选组用 <input type="radio">;避免给 <button> 添加 aria-pressed 或 aria-checked,因其需手动同步状态且易出错,而原生控件自动提供正确语义、键盘支持与辅助技术兼容性。

原生 <button> 本身不表达“当前状态”,它只是动作触发器;要让辅助技术准确播报“开/关”“选中/未选中”这类状态,必须换用语义匹配的原生控件,而不是给 <button> 塞 aria-pressed 或 aria-checked。
用 <input type="checkbox"> 替代 toggle button
绝大多数所谓“切换按钮”场景(如深色模式开关、通知开启/关闭),实际是二元状态控制,<input type="checkbox"> 天然支持:checked 属性存在即为“开启”,屏幕阅读器自动播报 “checked” 或 “not checked”,无需 JS 同步 aria-checked,也不会因 DOM 更新滞后导致语义错乱。
- 视觉样式可完全用 CSS 控制(
:checked + label或自定义伪元素) - 键盘交互天然支持:Tab 进入、空格切换、自动聚焦管理
- 表单提交时自动携带值(
name+value),无需额外监听 - 禁用状态直接写
disabled,辅助技术立刻感知
用 <input type="radio"> 实现语言切换或单选组
语言切换不是“按钮集合”,而是互斥选择——这正是 <input type="radio"> 的语义本职。给每个选项配 name(如 name="lang"),当前项加 checked,屏幕阅读器会读出 “English, radio button, selected” 这类完整语义,且自动处理组内焦点流转和状态同步。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 绝对不要用
<button>+aria-selected="true"——aria-selected不适用于按钮,读屏器会忽略或误报 - 不要手动设
aria-checked:原生input已内置该映射,JS 改checked属性即可,浏览器自动更新 ARIA 状态 - 若必须用自定义外观,包裹
<label>并隐藏原生 input(clip: rect(0 0 0 0)),保留语义链路
为什么给 <button> 加 aria-pressed 是高风险操作
除非你实现的是类似“播放/暂停”这种需即时反馈、且无表单集成需求的纯 UI 控制(比如音乐播放器主控),否则 aria-pressed 要求你全程手动维护三件事:DOM 属性、键盘响应(Enter/Space 切换)、视觉样式同步。稍有遗漏,语义就脱钩。
立即学习“前端免费学习笔记(深入)”;
- 常见错误:JS 更新了
aria-pressed="true",但没调用focus(),导致键盘用户无法感知焦点位置 - 更隐蔽的问题:在 React/Vue 中用
v-model或useState控制aria-pressed,但组件重渲染时未保证属性原子性更新,读屏器可能只读到中间态 - 原生
<button type="button">永远只表达“可点击”,不表达“已激活”——这是设计使然,强行覆盖反而破坏预期
真正容易被忽略的点是:语义正确 ≠ 视觉一致。你可以用 <input type="checkbox"> 实现任意样式的开关,但必须保留它的标签结构、name 和 id 关联,否则屏幕阅读器连“这是个什么控件”都判断不了——不是样式错了,是语义链断了。


















