WAI-ARIA(ウェイアリア)は、HTMLの要素だけでは伝わらない「これはボタンである」「いま開いている」「ここに通知が出た」といった情報を、スクリーンリーダーなどの支援技術へ伝えるためのW3C仕様です。role 属性と aria-* 属性で書きます。この記事では、仕様の位置づけ、ロールと属性の分類、よく使う属性の実装例、UI部品ごとにHTML要素で済むかの判断、そして使うとかえって悪化する場面を、Chrome 156で実際にアクセシビリティツリーを取り出した結果を交えて説明します。
まとめ:WAI-ARIAの要点
- WAI-ARIAは「Accessible Rich Internet Applications」の略で、現行の勧告はWAI-ARIA 1.2(2023年6月6日)。1.3は2026年6月4日付の草案です(2026年10月30日時点)。
- ARIAが変えるのは支援技術に見える情報(アクセシビリティツリー)だけです。キー操作・フォーカス・見た目は一切変わりません。
- 同じことがHTML要素でできるなら、ARIAではなく要素を使います。
<button>の代わりにrole="button"を書くと、Enterキー・スペースキーの処理を自前で書く必要が出ます。ARIAが本当に要るのは、タブやアプリ型メニューのように対応するHTML要素が無い部品です。 - WebAIM Million 2026では、ARIAのあるトップページは平均59.1件、ないページは平均42件のエラーが検出されました。ARIAは足すほど安全になるものではありません。
- 実務で出番が多いのは、名前(
aria-labelほか)、開閉状態(aria-expanded)、エラーの関連づけ(aria-invalid・aria-describedby)、動的な通知(aria-live)の4系統です。
WAI-ARIAの定義:HTMLに足りない役割と状態を支援技術へ渡す仕様
WAI-ARIAは、W3CのWeb Accessibility Initiative(WAI)の中にあるARIAワーキンググループが策定しています。WAIという組織そのものや、WCAG・ATAGとの関係はWAIとは?W3CのWCAG・WAI-ARIA・ATAGの関係と現行版【2026年】で整理しています。名前の「WAI」は組織名、「ARIA」が仕様名で、略して「ARIA」とだけ呼ぶことも多いです。
アクセシビリティツリーとARIAの関係
ブラウザはDOMとは別に、支援技術向けの「アクセシビリティツリー」を組み立てます。各ノードは役割(role)・名前(name)・状態(state)を持ち、スクリーンリーダーはこのツリーを読みます。ARIAはこのツリーの値を上書き・追加する手段です。
APG(ARIA Authoring Practices Guide)は冒頭の「Read Me First」で、ARIAのロール・状態・プロパティを「支援技術にとってのCSSのようなもの」と説明し、見出しに「No ARIA is better than Bad ARIA」(誤ったARIAならARIAが無い方がよい)を掲げています。CSSが見た目だけを変えて振る舞いを変えないのと同じで、ARIAもツリーの表示を変えるだけです。
WCAG 2.2では達成基準4.1.2「名前・役割・値(Name, Role, Value)」がこの層の要件にあたります。自作のUI部品は、名前・役割・状態がプログラムから取得できなければなりません。WCAG全体の構成はWCAGとは?最新版2.2の4原則・達成基準86件とAA適合の範囲を参照してください。
現行の版と関連仕様
| 文書 | ステータス | 日付 |
|---|---|---|
| WAI-ARIA 1.2 | 勧告(現行版) | 2023-06-06 |
| WAI-ARIA 1.3 | 草案(Working Draft) | 2026-06-04 |
| ARIA in HTML | 勧告 | 2026-08-11 |
| AccName 1.2(名前の計算) | 草案(Working Draft) | 2026-10-21 |
| Using ARIA | 廃止された草案 | 2026-02-24 |
日付はW3Cの公開版(WAI-ARIA 1.2・WAI-ARIA 1.3)を2026年10月30日に確認したものです。HTML要素ごとにどのロール・属性を付けてよいかを定めているのは「ARIA in HTML」です。以前よく引用された「Using ARIA」(いわゆるARIA使用のルール)は、2026年2月24日付で廃止された草案(Discontinued Draft)になりました。4つのルールは参照用に残されていますが、文書自身がその後の指針としてAPGを案内しているため、根拠としてはARIA in HTMLとAPGを使います。
ロール・ステート・プロパティの3分類
ARIAで書けるものは、要素が何であるかを示す ロール、ユーザー操作で変わる ステート(状態)、比較的変わらない プロパティ(性質)の3種類です。
role属性の6区分と代表ロール
WAI-ARIA 1.2の「Categorization of Roles」は、ロールを次の6区分に分けています。
| 区分 | 代表的なロール | 作者が使うか |
|---|---|---|
| 抽象ロール | widget, landmark, section など | 使用禁止 |
| ウィジェットロール | button, checkbox, tab, slider | 使う |
| 文書構造ロール | heading, list, img, note | 使う |
| ランドマークロール | banner, navigation, main, search | 使う |
| ライブリージョンロール | alert, status, log, timer, marquee | 使う |
| ウィンドウロール | dialog, alertdialog | 使う |
抽象ロールはロールの継承関係を定義するための概念で、仕様は「Authors MUST NOT use abstract roles in content」と明記しています。role="note" のような文書構造ロールは、本文から少し外れた補足を示すもので、HTMLに対応する要素がないため、ARIAを使う正当な場面の一つです。
<p>申込みは前日の17時まで受け付けます。</p>
<div role="note" aria-label="補足">
祝日の前日は15時で締め切ります。
</div>
note は名前を付けてもよいロールなので、注記が複数あるページでは aria-label で区別できます。本文の流れと関係が薄い囲み記事なら <aside>(complementary)が先の候補で、note は段落に添える短い補足に使います。
ステートとプロパティの違い
ステートは操作で頻繁に変わる値です。aria-expanded(開閉)、aria-checked(チェック)、aria-pressed(トグルの押下)、aria-disabled(無効)、aria-current(現在地。ナビゲーションの現在ページなら aria-current="page")がこれにあたり、JavaScriptで書き換え続けます。
プロパティは要素の性質で、ページ表示後はあまり変わりません。aria-label・aria-labelledby(名前)、aria-describedby(説明)、aria-controls(操作対象)、aria-haspopup などです。WAI-ARIA 1.2自身も「変更頻度の差は規則ではない」とし、aria-valuenow のように頻繁に変わるプロパティを挙げていますが、ステートかプロパティかにかかわらず、表示や操作に伴って値が変わる属性を洗い出すと、更新漏れを防げます。
ランドマークロールとHTML要素の対応
ランドマークは、スクリーンリーダー利用者が「ナビゲーションへ飛ぶ」「本文へ飛ぶ」ためのページ内の目印です。ほとんどはHTML要素が暗黙に持っているので、role を書く必要はありません。
| HTML要素 | 暗黙のロール | ランドマークになる条件 |
|---|---|---|
| header | banner | article・aside・main・nav・section の外 |
| nav | navigation | 常に |
| main | main | 常に |
| aside | complementary | body・mainをスコープとするとき、またはアクセシブルネームがあるとき |
| footer | contentinfo | article・aside・main・nav・section の外 |
| section | region | アクセシブルネームがあるとき |
| form | form | アクセシブルネームがあるとき |
| search | search | 常に |
Chrome 156で確かめると、名前のない <section> は generic になり、aria-labelledby で見出しを名前にした <section> だけが region になりました。同じ種類のランドマークが複数ある場合(グローバルナビとパンくずの2つの <nav> など)は、aria-label="パンくず" のように名前で区別します。
axe-coreの日本語版で「ドキュメントにmainランドマークがありません」と出るのは、<main> 要素も role="main" もない場合です。WebAIM Million 2026の調査では、main要素またはmainランドマークがあるトップページは46.1%にとどまっています。
ARIAを書く前にネイティブHTMLを選ぶ理由
HTML要素で済むものをdivとARIAで作り直すと、ネイティブ要素が備える操作を自前で実装する必要があります。ARIA in HTMLは、要素がすでに持っている暗黙のロールと同じ値を role に書くこと(例:<button role="button">)を「NOT RECOMMENDED」とし、不要なうえに意図しない結果を招きうると説明しています。
APGは「ARIAのロールは、HTMLのinput要素と違ってブラウザにキーボード操作やスタイルを提供させない」と書いています。Chrome 156.0.8078.76(headless、puppeteer-core経由)で次の4要素をそのまま読み込み、比べた結果が下の表です。2026年10月2日にChrome 154で測った結果とも一致しました。
<div onclick="save()">保存</div>
<div role="button" tabindex="0" onclick="save()">保存</div>
<button type="button" onclick="save()">保存</button>
<button type="button" aria-disabled="true" onclick="save()">保存</button>
| 要素 | ツリー上のロール | Tabで止まる | save()の実行 |
|---|---|---|---|
| div(属性なし) | generic | いいえ | マウスのみ |
div role="button" tabindex="0" |
button | はい | マウスのみ |
| button | button | はい | マウス・Enter・Space |
button aria-disabled="true" |
button(disabled) | はい | マウス・Enter・Space |
role="button" を付けたdivは、ツリー上はボタンに見えるのに、Enterでもスペースでも何も起きません。keydown でEnter、keyup でスペースを拾い(Chrome 156のbutton要素も、スペースはキーを離した時点でclickを発火しました)、tabindex="0" を付け、フォーカスの見た目を作って、ようやく <button> と同じになります。
aria-disabled="true" も同じ構図です。ツリー上は無効なのに、クリックイベントは発火し、Tabフォーカスも残ります。disabled 属性なら、クリックもフォーカスも止まる点が違いです。フォーカスを残して「なぜ押せないか」を読ませたい場合だけ aria-disabled を選び、クリック処理側で無効時に何もしない分岐を書きます。
WebAIM Million 2026に見るARIAとエラーの相関
WebAIMが上位100万サイトのトップページを調べたWebAIM Million 2026年版(2026年3月30日更新)では、ARIA属性は1ページ平均133個を超え、1年で27%増えました。ARIA(ランドマークロールを除く)を使っているページは82.7%です。そのうえで、ARIAのあるページは平均59.1件、ないページは平均42件のエラーが検出されています。
WebAIMは「ARIAがエラーを持ち込んだとは限らない(ARIAのあるページはより複雑でもある)」と注記しています。それでも、role="menu" を使ったトップページは5.7%で、それらのARIAメニューの22%が、必要なマークアップや操作の欠落で障壁を生んでいたという数字は、ロールを宣言しただけで実装が追いつかない典型です。
UI部品別のHTML要素とAPGパターンの選択基準
APGは、タブ・メニュー・コンボボックスなど30のパターンについて、必要なロール・状態とキー操作を定めています。部品を作る前に、まずHTML要素で済むかを見て、済まないものだけをAPGに沿ってARIAで作ります。
| 作りたいUI | 先に検討する要素 | APGのパターン | ARIAで作るときに要るもの |
|---|---|---|---|
| 開閉(FAQ・続きを読む) | details・summary | Disclosure | button+aria-expanded |
| モーダル | dialog+showModal() | Dialog (Modal) | フォーカスの閉じ込めと戻し |
| オン・オフの切り替え | input type=”checkbox” | Switch | role=”switch”+aria-checked |
| 選択肢から1つ選ぶ | select・input type=”radio” | Listbox・Radio Group | 矢印キーでの移動 |
| タブ | 対応する要素なし | Tabs | tablist・tab・tabpanel+左右矢印キー |
| アプリ型のメニュー | 対応する要素なし | Menu and Menubar | 矢印キー・Escape・文字キーでの移動 |
| ツールチップ | 対応する要素なし | Tooltip(合意前) | role=”tooltip”+aria-describedby |
表の下3行が、ARIAを書く正当な場面です。Tabsパターンでは、Tabキーでタブ列に入ると選択中のタブにフォーカスが移り、タブ間は左右の矢印キーで移動し(端では反対側へ回る)、もう一度Tabを押すとタブパネルへ出ます。タブ1つずつをTabキーで渡る作りは、このパターンに沿っていません。
メニューは判断が分かれます。Menu and Menubarパターンは、メニューを「デスクトップアプリのメニューバーのような、OSのメニューと同じ振る舞いをする部品」と定義し、矢印キーでの移動やEscapeでの復帰を求めます。常に表示されるmenubarにはTabキーで入れますが、項目の間は矢印キーで移動します。ボタンから開くmenuにはTabキーでもShift+Tabでも入れないため、開いたときにメニュー内の項目へフォーカスを移すのは作る側の責任です。APGはサイトナビゲーション用に、このメニューバーの例と、ボタンとリンクだけで組むDisclosure(開閉)型のナビゲーションメニューの例を両方載せています。リンクを並べたグローバルナビなら、<nav> とリンクに、サブメニューを開く <button aria-expanded> を足すだけの後者で足ります。前述のWebAIMの数字(ARIAメニューの22%で障壁)は、こうしたマークアップと操作の両方が欠けた結果として報告されたものです。
スイッチは、チェックボックスと機能は同じでも読み上げが変わります。Switchパターンは照明の例を挙げ、「Lights checkbox checked」より「Lights switch on」の方が分かりやすい場面があると書いています。ツールチップは、APGのページ自身が「作業中でタスクフォースの合意がない」と注記しているパターンなので、ツールチップの中にしか書いていない情報を作らない設計が安全です。
見出しやリストを role="heading"・role="list" で再現する必要もありません。モーダルなら <dialog> と showModal() がフォーカス管理まで受け持ちます。実装の詳細はドロワーUIとは?モーダル・ダイアログとの違いと使い分け、dialog要素での実装で扱っています。既存サイトの部品をこの表で棚卸しして直す作業は、Webアクセシビリティ対応として支援しています。
よく使うaria属性の使い方と実装例
名前と説明の指定:aria-label・aria-labelledby・aria-describedby
名前の付け方は優先順位が決まっています。aria-labelledby が最優先で、次に aria-label、その次に <label> や要素の中身のテキストです。画面に見えている文字があるなら、aria-labelledby でそれを参照すると、見た目と読み上げがずれません。
<!-- 見えている見出しを名前にする -->
<h2 id="cart-title">カートの中身</h2>
<ul aria-labelledby="cart-title">…</ul>
<!-- アイコンだけのボタンに名前を付ける -->
<button type="button" aria-label="メニューを開く">
<svg aria-hidden="true">…</svg>
</button>
<!-- 補足説明を関連づける -->
<label for="pw">パスワード</label>
<input id="pw" type="password" aria-describedby="pw-hint">
<p id="pw-hint">12文字以上で入力してください</p>
注意点は2つあります。1つ目は上書きです。Chrome 156で <label>メール</label> と aria-label="上書き名" を併用すると、名前は「上書き名」になりました。見えているラベルと読み上げ名が食い違うと、音声入力で「メールをクリック」と言っても反応しない原因になります(WCAG 2.5.3 Label in Name)。
2つ目は、名前を付けてはいけないロールです。WAI-ARIA 1.2の「Name prohibited」には generic、paragraph、presentation、strong、code など11ロールが並びます。素のdivやspanは generic なので、aria-label を付けても仕様上は無効です。ところがChrome 156のアクセシビリティツリーには「名前を付けたdiv」という名前がそのまま載りました。DevToolsに名前が出ていても仕様上は禁止で、支援技術がこの名前を読み上げる保証はありません。名前が要るなら、先に適切なロールを持つ要素に変えます。
開閉UI:aria-expandedとaria-controls
FAQのアコーディオンやメニューの開閉ボタンには、ボタン側に aria-expanded を付け、開閉のたびに更新します。aria-controls は開閉対象のidを指します。
<button type="button" id="faq-btn" aria-expanded="false" aria-controls="faq-panel">
送料はいくらですか
</button>
<div id="faq-panel" hidden>全国一律550円です。</div>
<script>
const btn = document.getElementById('faq-btn');
const panel = document.getElementById('faq-panel');
btn.addEventListener('click', () => {
const open = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!open));
panel.hidden = open;
});
</script>
Chrome 156で動かすと、クリック前は expanded=false、クリック後は expanded=true になり、Enterキーでもう一度押すと false に戻りました。<button> を使っているので、キー操作のコードは書いていません。また、パネルが hidden の間はツリーに controls の関係が出ず、表示後に現れました。参照先が非表示のあいだは関係が支援技術に渡らない前提で、状態は aria-expanded の値で伝えます。
開閉だけで十分なら、JavaScriptもARIAも要らない <details> と <summary> を先に検討します。APGのAccordionパターン(見出し+ボタン+パネル)は、複数パネルをまとめて扱う場合の設計です。
フォームのエラー:aria-invalidとaria-describedby
入力エラーは、項目に aria-invalid="true" を付け、エラーメッセージの要素を aria-describedby で関連づけます。送信時にフォーカスを誤った項目へ戻すと、支援技術には名前とあわせて、エラー・補足の順に連結された説明(description)が渡ります。
<form id="signup" novalidate>
<label for="email">メールアドレス</label>
<input id="email" type="email" required aria-describedby="email-hint">
<p id="email-hint">例:[email protected]</p>
<p id="email-error"></p>
<button>登録</button>
</form>
<script>
const form = document.getElementById('signup');
const email = document.getElementById('email');
const err = document.getElementById('email-error');
form.addEventListener('submit', (e) => {
e.preventDefault();
if (!email.checkValidity()) {
err.textContent = email.validity.valueMissing
? 'メールアドレスを入力してください'
: 'メールアドレスの形式が正しくありません';
email.setAttribute('aria-invalid', 'true');
email.setAttribute('aria-describedby', 'email-error email-hint');
email.focus();
} else {
err.textContent = '';
email.removeAttribute('aria-invalid');
email.setAttribute('aria-describedby', 'email-hint');
}
});
</script>
Chrome 156で「abc」と入れて送信した後のツリーでは、説明(description)が「メールアドレスの形式が正しくありません 例:[email protected]」になり(空欄で送信すると「メールアドレスを入力してください 例:[email protected]」)、invalid=true、フォーカスは入力欄に戻っていました。aria-describedby は空白区切りで複数のidを並べられ、並べた順に連結されます。WAI-ARIA 1.3草案では aria-errormessage がidの参照リストに変わりますが、現行の1.2では単一のidです。
動的な通知:aria-liveとstatusロール
「カートに追加しました」のように、フォーカスを動かさずに知らせたい変化はライブリージョンで伝えます。aria-live の値は off・polite(読み上げ中の内容を終えてから通知)・assertive(すぐ割り込んで通知)の3つです。ライブリージョンロールには暗黙の値があり、status と log は polite、alert は assertive、timer と marquee は off です。
<!-- ページ読み込み時から空の領域を置いておく -->
<div role="status" id="cart-status"></div>
<button type="button" id="add">カートに追加</button>
<script>
let count = 0;
document.getElementById('add').addEventListener('click', () => {
count += 1;
document.getElementById('cart-status').textContent = `カートに${count}点あります`;
});
</script>
Chrome 156では、role="status" の要素に live=polite と atomic=true が付いていました。atomic が true なので、変更を通知する際は領域全体を提示する指定になります。実際の読み上げは支援技術でも確認します。
よくある失敗は、通知のたびにライブリージョンの要素ごと挿入することです。支援技術は既存の領域の変化を監視するので、空の領域をページ読み込み時から置いておき、中身だけを書き換えます。assertive は読み上げ中の内容を中断させるので、使うのはセッション切れの警告のような緊急の通知だけです。DOMを書き換えずに読み上げを依頼する新しい方法はARIA Notify(ariaNotify())の使い方:aria-liveとの違い・priorityの指定と対応ブラウザで紹介しています。
aria-hidden="true" は要素とその子孫をツリーから外します。装飾アイコンや、隣のテキストと重複する画像に使います。親に付けると子に aria-hidden="false" を書いても戻りません。
ARIA in HTMLは、aria-hidden を付けてよい要素からフォーカス可能な要素と body を除いています。フォーカス可能な要素を含む親に付けた場合も、同じ問題が起きます。Chrome 156で aria-hidden="true" のdivの中にボタンを置くと、ボタンはツリーから消えたのに、Tabキーではフォーカスが止まりました。スクリーンリーダー利用者には「何もない場所にフォーカスがある」状態になります。モーダルの背景を隠すなら、aria-hidden ではなく inert 属性か showModal() を使えば、フォーカスもまとめて止まります。WebAIM Million 2026の集計では、トップページ1つあたりの aria-hidden="true" は平均23.3個で、2025年の18個から30%増でした。
JavaScriptからの操作:setAttributeとARIA反映プロパティ
ARIA属性は setAttribute('aria-expanded', 'true') のほか、el.ariaExpanded = 'true' のようなDOMプロパティでも書けます。WAI-ARIA 1.2は、全てのARIA属性がnull許容のDOMString(文字列)として反映されると定めています。真偽値型の属性でも値は文字列です。Chrome 156では el.ariaExpanded = true と書いても文字列の 'true' に変換され、getAttribute('aria-expanded') も 'true' を返しました。落とし穴は読み出し側で、閉じている状態でも 'false' という文字列が返るため、if (el.ariaExpanded) は常に真になります。判定は el.ariaExpanded === 'true' と書きます。未設定の属性を読むと null が返ります。
実装後の確認方法:アクセシビリティツリーと読み上げ
ARIAは見た目に出ないため、目視では正誤が分かりません。確認は3段で行います。
1段目はブラウザのツリー表示です。Chrome DevToolsのElementsパネルにあるAccessibilityタブで、ロール・名前・状態が意図どおりかを1要素ずつ見られます。2段目は自動チェックで、axe-coreやLighthouseが、禁止属性・名前なしのボタン・mainランドマーク不在などを拾います。ただしWAIの評価ツール選定ガイドは「ツールで全部はできない。自動化できない確認があり、人の手が要る」と書いており、ツールが合格でも操作できる保証にはなりません。テストコードに組み込むなら、Playwrightの toMatchAriaSnapshot でツリーを固定できます(Playwright aria snapshotの使い方|toMatchAriaSnapshotの書き方と更新手順【1.63対応】)。
3段目がスクリーンリーダーでの実操作です。ライブリージョンの読み上げタイミングや、aria-describedby が読まれる順序は、ツリーを見ても分かりません。自動・手動・当事者による検証の分担はWebアクセシビリティ診断の進め方:自動・手動・当事者検証の三層設計と外注の判断基準で整理しています。NVDAとVoiceOverでの確認手順はWebアクセシビリティのスクリーンリーダー対応:NVDA・VoiceOverでの実機検証と読み上げ順序の設計にまとめています。
WAI-ARIA 1.3草案の追加属性・ロール・API
2026年6月4日付の1.3草案は、1.2にない次の要素を含みます。
aria-description:aria-describedbyと違い、参照先の要素なしで説明文を直接書ける属性aria-braillelabel・aria-brailleroledescription:点字ディスプレイ向けに別の表記を渡す属性comment・suggestion・mark・sectionheader・sectionfooterなどの新しいロールariaNotifyAPI、ルート要素へのaria-hidden="true"の禁止
草案の段階では仕様が変わる可能性があり、ブラウザと支援技術の対応もそろっていません。本番のUIで新しい属性に頼る場合は、無視されても情報が欠けない作りにしておきます。
よくある質問
WAI-ARIAの読み方と、何の略ですか?
「ウェイアリア」と読みます。WAIはWeb Accessibility Initiative、ARIAはAccessible Rich Internet Applicationsの略です。
React AriaとWAI-ARIAは同じものですか?
別物です。WAI-ARIAはW3Cの仕様で、React AriaはAdobeが公開しているReact向けのライブラリです。React AriaはWAI-ARIAに沿った属性とキー操作を部品に組み込みます。詳しくはReact Ariaとは?React Aria Componentsの使い方・Tailwind連携【v1.22対応】を参照してください。
display:none は画面からもアクセシビリティツリーからも要素を消します。aria-hidden="true" はツリーから外すだけで、画面には表示されたままで、フォーカス可能な要素ならTabキーでも止まります。全員から隠すなら display:none か hidden 属性、見た目は残して読み上げだけ省くなら aria-hidden が向いています。
aria-labelとimgのalt属性はどちらを使えばよいですか?
画像にはalt属性を使います。altはHTMLの標準の仕組みで、画像が読み込めないときの代替表示にもなります。aria-label はアイコンだけのボタンなど、要素に見えるテキストがない場合に名前を付ける手段です。
ARIAのルールは4つですか、5つですか?
W3Cの「Using ARIA」に残っているのは4つです。(1) 必要な意味と動作を持つHTML要素・属性があるならそれを使う、(2) どうしても必要な場合以外は要素本来の意味を変えない、(3) ARIAで作った操作部品は全てキーボードで使えるようにする、(4) フォーカスできる要素に role="presentation" や aria-hidden="true" を付けない。web.devの「ARIA と HTML」はこれに「インタラクティブ要素にアクセシブルな名前を付ける」を加えた5つのルールとして紹介しています。どちらで覚えても、中身は本文の判断表と同じです。