aria-* 属性とは何か
ARIA(Accessible Rich Internet Applications)の aria-* 属性は、UI の「意味・状態・関係」を支援技術(スクリーンリーダーなど)に伝えるための仕組みです。原則としてネイティブ要素(button、a、dialog、input など)を優先し、どうしてもカスタム要素(div や span)で UI を作る場合に限って、role と aria-* で意味を補います。ここが重要です:見た目だけ整えても“伝わらない”。役割(role)、名前(ラベル)、状態(オン/オフ)、関係(何を制御するか)を揃えると、誰にとっても使いやすい UI になります。
ネイティブ優先と最小限の ARIA
適切な HTML 要素を使う
ボタンは button、リンクは a、モーダルは dialog、見出しは h1〜h6 という“正しい要素選び”だけで、役割・キーボード操作・多くのアクセシビリティが自然に手に入ります。ここが重要です:div に role=”button” を付けるのは最終手段。ネイティブで表現できるなら、それが最も強くて安全です。
カスタム要素には role を与える
どうしても div でボタンの見た目を作るなら role=”button” と tabindex=”0″ を付け、Enter/Space をハンドリングし、aria-* で状態を同期します。ここが重要です:role は“本来の意味がない要素”にだけ付ける。ネイティブ要素に重ねるのは冗長で、誤用です。
名前付けと説明(aria-label / aria-labelledby / aria-describedby)
視覚テキストがない UI に名前を付ける
<button id="save" aria-label="保存">💾</button>
HTMLアイコンだけのボタンは aria-label で動作名を付けます。ここが重要です:短く一意に。「保存」「閉じる」「検索」のように、行為が明確に伝わる言葉にします。
既存テキストを“名前”として参照する
<h2 id="title">設定</h2>
<button aria-labelledby="title">編集</button>
HTMLaria-labelledby は別要素のテキストをラベルにできます。ここが重要です:文言の重複を避け、翻訳や変更の負担を減らせます。
補足説明を添える
<input id="email" aria-describedby="hint">
<small id="hint">会社のメールを入力してください</small>
HTMLaria-describedby はヒントや注意書きの紐付けです。ここが重要です:名前(label)は“識別”、説明(describedby)は“補足”。役割を混同しない。
状態と関係(aria-expanded / aria-controls / aria-hidden など)
開閉 UI を同期する
<button id="toggle" aria-expanded="false" aria-controls="panel">詳細</button>
<div id="panel" hidden>内容…</div>
<script>
const btn = document.getElementById("toggle");
const panel = document.getElementById("panel");
btn.addEventListener("click", () => {
const open = btn.getAttribute("aria-expanded") !== "true";
btn.setAttribute("aria-expanded", String(open));
panel.hidden = !open; // 視覚と状態を一致させる
});
</script>
HTMLaria-controls は「どれを制御するか」、aria-expanded は「開いているか」。ここが重要です:視覚(hidden/クラス)と aria の真偽を常に一致させ、単一情報源の設計にします。
見えないものは本当に“隠す”
<div id="toast" aria-hidden="true">保存しました</div>
HTML非表示なら aria-hidden=”true” を付け、読み上げ対象から外します。ここが重要です:視覚(visibility/display)、操作不可(pointer-events/inert)、読み上げ(aria-hidden)を揃えると事故が減ります。
トグルの“押下状態”を伝える
<button id="notify" aria-pressed="false" class="btn">通知</button>
<script>
const b = document.getElementById("notify");
b.addEventListener("click", () => {
const next = b.getAttribute("aria-pressed") !== "true";
b.setAttribute("aria-pressed", String(next));
b.classList.toggle("is-on", next);
});
</script>
HTML押下のトグルは aria-pressed、チェックの有無は aria-checked。ここが重要です:UI の種類に合わせて適切な“状態属性”を選ぶ。
ライブ領域と処理中(aria-live / aria-busy)
画面更新の読み上げ
<div id="ann" aria-live="polite"></div>
<script>
ann.textContent = "保存しました";
</script>
HTMLaria-live は動的更新の読み上げ優先度(polite/assertive)。ここが重要です:頻繁な通知で assertive を乱用しない。重要度に応じて控えめに。
“処理中”を明示
<button id="send" aria-busy="false">送信</button>
<script>
send.addEventListener("click", async () => {
send.setAttribute("aria-busy", "true");
send.disabled = true;
try { await new Promise(r => setTimeout(r, 800)); }
finally {
send.setAttribute("aria-busy", "false");
send.disabled = false;
}
});
</script>
HTMLここが重要です:視覚(is-loading)、操作不可(disabled)、読み上げ(aria-busy)を同時に切り替え、状態ズレをなくします。
キーボードとフォーカス(role と組み合わせる)
カスタムボタンを“ネイティブ同等”に
<div id="toggle" role="button" tabindex="0" aria-pressed="false">通知</div>
<script>
const t = document.getElementById("toggle");
const set = on => { t.setAttribute("aria-pressed", String(on)); t.classList.toggle("is-on", on); };
t.addEventListener("click", () => set(t.getAttribute("aria-pressed") !== "true"));
t.addEventListener("keydown", e => {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); t.click(); }
});
</script>
HTMLここが重要です:Enter/Space を実装し、tab でフォーカスできるようにする。ネイティブの操作体系を再現します。
モーダルのフォーカスを制御
<div id="modal" role="dialog" aria-modal="true" aria-labelledby="m-title" hidden>
<h2 id="m-title">設定</h2>
<button id="close">閉じる</button>
</div>
<script>
const modal = document.getElementById("modal");
const close = document.getElementById("close");
function openModal() { modal.hidden = false; close.focus(); }
function closeModal() { modal.hidden = true; /* 元のトリガへ focus を戻す */ }
</script>
HTMLここが重要です:aria を付けるだけでは不十分。フォーカストラップやスクロールロックまで含めて“動作の整合”を完成させます。
落とし穴と回避策(誤用・重複・ID整合・テスト)
不要な ARIA を付けない
button に role=”button” は冗長、a に aria-pressed は用途が違うなど、過剰な ARIA はノイズです。ここが重要です:ARIA は必要最小限。ネイティブで足りるなら何も足さない。
参照 ID の整合性
aria-labelledby/aria-controls の ID が存在しないと関係は壊れます。ここが重要です:生成・削除のコードで ID 整合を常に確認し、欠けたら処理をスキップする。
実機で検証する
スクリーンリーダー(NVDA、VoiceOver など)とキーボード操作で、名前・役割・状態が期待通り伝わるか確認します。ここが重要です:見た目だけのテストでは不十分。操作と読み上げを同時にチェックするのが本当の品質保証です。
まとめ
ARIA は「意味・状態・関係」を支援技術に伝えるための言語です。ネイティブ要素を優先し、カスタム要素には role と aria-* を最小限で補う。名前は aria-label/aria-labelledby、説明は aria-describedby、開閉は aria-expanded+aria-controls、非表示は aria-hidden、トグルは aria-pressed/aria-checked、更新通知は aria-live、処理中は aria-busy。視覚(hidden/クラス)、操作(キーボード/フォーカス)、読み上げ(aria)を“同じ真偽”で同期すれば、初心者でも確実で心地よいアクセシブル UI を作れます。

