無障礙網頁(Accessibility / a11y)
為什麼無障礙很重要?
比喻:無障礙就像建築物的無障礙坡道 ♿
不只輪椅使用者需要——推嬰兒車、搬重物的人也受益。 無障礙網頁不只幫助視障者——也幫助所有人。
ARIA 屬性
<!-- role — 定義元素的角色 -->
<div role="alert">操作成功!</div> <!-- 螢幕閱讀器會立刻朗讀 -->
<div role="navigation">...</div> <!-- 等同 <nav> -->
<div role="button" tabindex="0">點我</div> <!-- 讓 div 被當作按鈕 -->
<!-- aria-label — 給沒有文字的元素加標籤 -->
<button aria-label="關閉">✕</button> <!-- 螢幕閱讀器會唸「關閉」 -->
<input aria-label="搜尋"> <!-- 沒有 label 時用 aria-label -->
<!-- aria-hidden — 隱藏裝飾性元素 -->
<span aria-hidden="true">🎉</span> <!-- 螢幕閱讀器會忽略 -->
<!-- aria-live — 動態更新的區域 -->
<div aria-live="polite"> <!-- 內容改變時通知使用者 -->
目前有 5 個通知
</div>
<!-- aria-expanded — 展開/收合狀態 -->
<button aria-expanded="false" <!-- 告知選單是收合的 -->
aria-controls="menu">
選單
</button>
<ul id="menu" hidden>...</ul>
鍵盤操作
<!-- tabindex 控制 Tab 鍵順序 -->
<input tabindex="1"> <!-- 第一個被 Tab 到 -->
<input tabindex="2"> <!-- 第二個 -->
<div tabindex="0">可聚焦的 div</div> <!-- 0 = 照 DOM 順序 -->
<div tabindex="-1">程式才能聚焦</div> <!-- -1 = Tab 跳過,JS 可聚焦 -->
// 自訂元素支援 Enter 和 Space 觸發
document.querySelector('[role="button"]').addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
// 執行按鈕動作
}
});
無障礙清單
| 項目 | 做法 |
|---|---|
| 圖片 | 所有 <img> 都要有 alt |
| 表單 | 每個 input 都要有 <label> |
| 色彩 | 對比度至少 4.5:1 |
| 鍵盤 | 所有功能可用鍵盤操作 |
| 語意 | 使用正確的語意標籤 |
| 動態 | 用 aria-live 通知更新 |
| 焦點 | 焦點指示器清楚可見 |
| 結構 | 標題層級正確(h1→h2→h3) |
常見錯誤
<!-- ❌ 用 div 當按鈕 -->
<div onclick="doSomething()">點我</div>
<!-- ✅ 用真正的按鈕 -->
<button onclick="doSomething()">點我</button>
<!-- ❌ 用顏色作為唯一指示 -->
<span style="color: red">錯誤</span>
<!-- ✅ 除了顏色,加上文字或圖示 -->
<span style="color: red">❌ 錯誤:請輸入有效的 Email</span>
<!-- ❌ 自動播放有聲音的影片 -->
<video autoplay>
<!-- ✅ 自動播放要靜音 -->
<video autoplay muted>