此技能帮助开发者实现前端无障碍设计。
<!-- ❌ 错误 -->
<div class="header">标题</div>
<div class="nav">导航</div>
<div class="button">按钮</div>
<!-- ✅ 正确 -->
<header>标题</header>
<nav>导航</nav>
<button>按钮</button>
<!-- ✅ 正确 -->
<form>
<label for="name">姓名</label>
<input id="name" type="text" required>
<label for="email">邮箱</label>
<input id="email" type="email" required>
<fieldset>
<legend>性别</legend>
<label>
<input type="radio" name="gender" value="male"> 男
</label>
<label>
<input type="radio" name="gender" value="female"> 女
</label>
</fieldset>
</form>
<!-- 角色 -->
<div role="navigation" aria-label="主导航"></div>
<div role="button" tabindex="0" aria-pressed="false"></div>
<!-- 状态 -->
<input type="text" aria-required="true" aria-invalid="false">
<button aria-disabled="true">禁用按钮</button>
<!-- 关系 -->
<div role="tablist" aria-label="选项卡">
<button role="tab" aria-selected="true" id="tab1">标签 1</button>
<button role="tab" aria-selected="false" id="tab2">标签 2</button>
</div>
<div role="tabpanel" aria-labelledby="tab1">内容 1</div>
<div role="tabpanel" aria-labelledby="tab2" hidden>内容 2</div>
<!-- 对话框 -->
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">确认操作</h2>
<p>您确定要删除此项目吗?</p>
<button>确定</button>
<button>取消</button>
</div>
<!-- 滑块 -->
<div role="slider"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="50"
aria-label="音量">
</div>
// 焦点陷阱
function createFocusTrap(element) {
const focusableElements = element.querySelectorAll(
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
const firstElement = focusableElements[0]
const lastElement = focusableElements[focusableElements.length - 1]
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault()
lastElement.focus()
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault()
firstElement.focus()
}
}
}
})
}
// 模态框焦点管理
function manageModalFocus(modal) {
const originalFocus = document.activeElement
modal.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
modal.close()
originalFocus?.focus()
}
})
modal.addEventListener('close', () => {
originalFocus?.focus()
})
}
// 键盘事件处理
function handleKeyDown(e) {
switch (e.key) {
case 'Enter':
case ' ': // 空格键
e.preventDefault()
this.handleClick()
break
case 'ArrowUp':
e.preventDefault()
this.handlePrevious()
break
case 'ArrowDown':
e.preventDefault()
this.handleNext()
break
case 'Home':
e.preventDefault()
this.handleHome()
break
case 'End':
e.preventDefault()
this.handleEnd()
break
}
}
// 添加键盘支持
const button = document.querySelector('.custom-button')
button.setAttribute('tabindex', '0')
button.addEventListener('keydown', handleKeyDown)
<!-- 图像描述 -->
<img src="logo.png" alt="公司 logo">
<!-- 复杂图像 -->
<img src="chart.png" alt="月度销售图表" aria-describedby="chart-desc">
<div id="chart-desc">
图表显示 2023 年 1-12 月的销售数据,12 月达到峰值。
</div>
<!-- 隐藏但可访问的文本 -->
<button>
<span class="sr-only">添加到购物车</span>
<svg aria-hidden="true">...</svg>
</button>
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
// 通知屏幕阅读器
function announce(message) {
const liveRegion = document.getElementById('live-region')
if (liveRegion) {
liveRegion.textContent = message
}
}
// 使用 aria-live
<div aria-live="polite" id="live-region"></div>
// 实时区域级别
// aria-live="polite" - 等待空闲时宣布
// aria-live="assertive" - 立即宣布
# Lighthouse
npx lighthouse https://example.com --output=html
# axe-core
npm install axe-core
npx axe http://localhost:3000
# 在线工具
# https://webaim.org/resources/contrastchecker/
# https://contrast-ratio.com/
/* ✅ 良好的对比度 */
.text-primary {
color: #333333; /* 黑色文本 */
background-color: #ffffff; /* 白色背景 */
/* 对比度: 12.63:1 */
}
.text-secondary {
color: #666666; /* 深灰色文本 */
background-color: #ffffff; /* 白色背景 */
/* 对比度: 5.34:1 */
}
/* ❌ 差的对比度 */
.text-poor {
color: #999999; /* 浅灰色文本 */
background-color: #f5f5f5; /* 浅灰色背景 */
/* 对比度: 1.65:1 */
}
/* 使用相对单位 */
:root {
font-size: 16px;
}
body {
font-size: 1rem; /* 16px */
}
h1 {
font-size: 2rem; /* 32px */
}
/* 媒体查询 */
@media (max-width: 768px) {
:root {
font-size: 14px;
}
}
/* 触摸目标大小 */
button, a {
min-width: 44px;
min-height: 44px;
padding: 8px 16px;
}
/* 间距 */
button + button {
margin-left: 16px;
}
input + input {
margin-top: 12px;
}
共 1 个版本