纯html组件库默认不具备可访问性,因为和无语义、无键盘行为、不参与表单流程,屏幕阅读器仅读作“group”或“text”;用替代会导致空格/enter无效、:disabled不生效、焦点样式丢失。

为什么纯 HTML 组件库默认不具备可访问性
因为 <div> 和 <code><span></span> 没有语义、没有键盘行为、不参与表单流程,屏幕阅读器只会读作“group”或“text”,不会告诉你这是按钮、开关还是导航项。你写的 <div class="c-button">提交</div> 在辅助技术眼里和一段普通文字没区别。
常见错误现象:
- 用
<div role="button"> 替代 <code><button></button>,结果空格/Enter 键无效、:disabled不生效、焦点样式丢失 - 卡片组件外层用
<div class="c-card">,导致 RSS 抓取失败、SEO 权重分散、视障用户无法识别内容边界 <li>自定义下拉菜单没暴露 <code>aria-expanded和aria-controls,屏幕阅读器无法告知当前状态 - 独立内容单元 → 用
<article></article>(如商品卡片、用户评论) - 导航区块 → 用
<nav></nav>,内部必须是<a></a>或<button></button> - 主内容区 → 用
<main></main>,且全页仅一个 - 图文说明 → 用
<figure><figcaption></figcaption></figure>,不是<div><div> <h3>如何让自定义元素支持键盘与表单集成</h3> <p>注册 <code>customElements.define('x-toggle', XToggle)后,它默认仍是“哑巴节点”。要让它被读屏软件识别为开关、能响应空格键、能被form.reset()重置,必须满足三项硬性条件:- 构造函数第一行调用
super(),继承HTMLElement - 声明
static formAssociated = true(开启表单集成开关) - 在 Shadow DOM 内挂载真实
<input type="checkbox">,并通过 getter/setter 同步checked、disabled、value
attributeChangedCallback是另一个高频翻车点:监听checked时,newValue是字符串"true"或null,不是布尔值。写成input.checked = newValue === 'true'才正确;disabled则需判断newValue !== null,因为disabled=""和disabled="false"都传"",但后者不该禁用。HTML 模板中 data-bind 的安全用法
<template id="c-card"></template>里写<h3 data-bind="title"></h3>看似简单,但绑定逻辑若不严谨,会破坏可访问性:- 禁止在
data-bind值里直接插入 HTML 字符串(XSS 风险),应只绑定原始类型:textContent赋值,innerHTML留给明确需要富文本的场景并手动转义 - 避免用连字符命名绑定点(如
data-bind="user-name"),JS 中访问需用el.dataset.userName,易拼错;推荐data-bind="user_name"或data-bind="userName" - 图片
<img data-bind="src">必须同步设置alt,否则生成的 DOM 会缺失替代文本——不能靠“后续补”
模板实例化时,必须用
document.importNode(tmpl.content, true),不是cloneNode(true):前者保留<input checked>状态,后者会丢掉所有用户交互痕迹。CSS 设计令牌对无障碍的实际影响
写死
margin: 8px看似省事,但会导致三类可访问性失效:- 高 DPR 屏幕(如 MacBook Retina)上像素模糊,视觉对比度下降
- 系统级缩放(Windows 125% / macOS Zoom)时,间距无法等比放大,布局崩坏
- 深色模式切换时,
!important会锁死颜色变量覆盖,导致主题色无法生效
正确做法是统一用 CSS 自定义属性:
:root { --c-space-md: 0.75rem; --c-color-text: #333; --c-color-text-inverted: #fff; } .c-card__header { padding: var(--c-space-md); } @media (prefers-color-scheme: dark) { :root { --c-color-text: #ddd; } }所有组件内距、边框、阴影、字体大小都基于这些变量。禁用
px和!important不是教条,而是确保下游项目能通过覆盖变量实现无障碍适配——比如把--c-space-md改成1.25rem以满足 WCAG 2.2 的间距要求。 - 构造函数第一行调用
真正可访问的起点不是加 ARIA,而是选对原生标签:











