1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

aria-labelは「読み上げの上書き」ではない — アクセシブルな名前の正しい付け方

1
Posted at

aria-label は、たぶんARIA属性の中でいちばん気軽に書かれていて、いちばん間違って書かれている属性です。

  • 「スクリーンリーダーで変な読み方をされるから aria-label で直そう」
  • div に説明を付けておけば伝わるだろう」
  • 「アクセシビリティ対応、とりあえず aria-label を足しておこう」

このどれも、多くの場合は効いていないか、むしろ体験を悪くしています

この記事では、aria-label が何をする属性なのかを整理したうえで、実際に効く場面・効かない場面・代わりに使うべき手段を、コード付きでまとめます。

TL;DR

  • aria-label は「読み上げ方」を変える属性ではなく、アクセシブルな名前(accessible name)を上書きする属性
  • div / span / p / em / strong などには仕様上、名前を付けられない。書いても無視される
  • 可視テキストと違う名前を付けると、音声操作ユーザーがその要素を操作できなくなる(WCAG 2.5.3)
  • 優先順位は 可視テキスト → label/altaria-labelledbyaria-labelaria-label は最後の手段
  • aria-label は機械翻訳で翻訳されない。多言語を考えるなら視覚的非表示テキストのほうが強い

1. 前提:aria-labelは「アクセシブルな名前」を決める属性

支援技術は、要素を ロール(role)+ アクセシブルな名前(accessible name) の組で扱います。

<button>送信</button>

これはスクリーンリーダーに「送信、ボタン」と伝わります。送信 が名前、ボタン がロールです。

aria-label はこの「名前」を差し替える属性です。読み方(音)を指定する属性ではありません。 ここを取り違えると、以降のすべての誤用が生まれます。

名前の決定順序

名前は複数の候補から、決まった優先順位で1つに決まります(ボタンやフォーム部品の場合)。

優先度 ソース
1 aria-labelledby aria-labelledby="heading-1"
2 aria-label aria-label="閉じる"
3 ネイティブの仕組み <label>alt<legend><caption>
4 要素の内容 <button>送信</button> の「送信」
5 フォールバック titleplaceholder

重要なのは、aria-label は 3 と 4 を潰すということです。つまり aria-label を書いた瞬間、<label> も要素内のテキストも名前としては消えます。

<!-- ❌ 読み上げは「削除、ボタン」だけ。中の文字は名前として消える -->
<button aria-label="削除">この投稿を削除する</button>

これが後述する「音声操作で押せないボタン」を生みます。


2. aria-labelが正しく効く場面

先に「使っていい場面」を押さえておきます。共通点は、ロールが名前を受け付ける要素であることと、他に名前の出しどころがないことです。

2-1. アイコンのみのボタン

一番の正統な用途です。

<button type="button" aria-label="閉じる">
  <svg aria-hidden="true" focusable="false" width="16" height="16">
    <use href="#icon-close" />
  </svg>
</button>

ポイント2つ。

  • 装飾用のSVGには aria-hidden="true" を付けて、アイコン自体を名前計算から外す
  • focusable="false" は古いIE/Edge対策の名残ですが、付けておいて損はない

なお、名前に「ボタン」を含めないこと。ロールが自動で読まれるので「閉じるボタン、ボタン」になります。

<!-- ❌ 冗長 -->
<button aria-label="閉じるボタン"></button>

2-2. 同種のランドマークを区別する

navsection が複数あるとき、名前がないと支援技術のランドマーク一覧で見分けが付きません。

<nav aria-label="グローバル"></nav>
<nav aria-label="パンくず"></nav>
<nav aria-label="ページ送り"></nav>

ここでも「〜ナビゲーション」と書かないこと。ロール名が重複します。

補足:<section>名前を付けたときだけ region ランドマークになります。名前がない <section>generic 扱いで、ランドマークになりません。

<!-- ✅ region ランドマークになる -->
<section aria-label="お知らせ"></section>

<!-- これは単なる generic。ランドマーク一覧に出ない -->
<section></section>

2-3. iframe

iframe は中身が別文書なので、外から名前を与える必要があります。

<iframe src="/map" title="店舗所在地の地図"></iframe>

iframe については title 属性が伝統的で、こちらで十分です(aria-label でも可)。

2-4. その他

  • role="img" を付けた絵文字・アスキーアート・複数要素で構成された図
  • <table><caption> を置けない事情があるとき(置けるなら <caption> が上)
  • <input type="search"> などで、デザイン上どうしても可視ラベルを置けないとき(ただし 3-4 を読んでから)

3. よくある誤用5選

誤用① div / span に付ける

これが最も多い誤用です。書いても効きません。

<!-- ❌ 無視される -->
<div aria-label="お知らせ">
  <p>本日はメンテナンスを実施します</p>
</div>

<span aria-label="必須">*</span>

WAI-ARIA仕様には「名前を付けられないロール(Roles which cannot be named)」の一覧があり、そこに generic が含まれています。divspan の暗黙のロールはこの generic です。

同じく名前を付けられないロールと、対応するHTML要素:

ロール HTML要素
generic div, span
paragraph p
emphasis em, i
strong strong, b
code code
caption caption, figcaption
deletion / insertion del / ins
subscript / superscript sub / sup
mark mark
presentation / none role="presentation" を付けた要素
time time

実機でも挙動はバラバラです。無視するスクリーンリーダー、名前だけ読んで中身を読まないもの、両方読むものが混在します。「一部の環境では効くが、一部では壊れる」は対応として成立しません。

どうするか

意味のある区切りなら、名前を受け付ける要素・ロールに変えます。

<!-- ✅ section に名前を付ければ region になる -->
<section aria-label="お知らせ">
  <p>本日はメンテナンスを実施します</p>
</section>

<!-- ✅ 見出しがあるなら labelledby で参照するのがベター -->
<section aria-labelledby="news-heading">
  <h2 id="news-heading">お知らせ</h2>
  <p>本日はメンテナンスを実施します</p>
</section>

必須マークのような「テキストの言い換え」は、素直に可視テキストか視覚的非表示テキストにします。

<!-- ✅ -->
<label for="email">
  メールアドレス
  <span aria-hidden="true">*</span>
  <span class="visually-hidden">(必須)</span>
</label>
<input id="email" type="email" required>

例外

暗黙のロールが generic から変わるケースだけは効きます。

<!-- ロールが group になるので名前を付けられる -->
<div popover aria-label="設定"></div>

<!-- role を明示すれば当然OK -->
<div role="button" tabindex="0" aria-label="閉じる">×</div>

誤用② 可視テキストと違う名前を付ける

<!-- ❌ 見えているのは「送信」、名前は「確定」 -->
<button aria-label="確定">送信</button>

これは2種類のユーザーを壊します。

音声操作ユーザー:「送信をクリック」と発話しても、名前が「確定」なので一致せず、そのボタンを押せません。マウスもキーボードも使いづらい人にとって、操作手段が消えることを意味します。

ロービジョン・読字に困難のあるユーザー:拡大表示や読み上げを併用していると、見えている文字と読まれる文字が違って混乱します。

これは WCAG 2.1 の達成基準 2.5.3 Label in Name(レベルA) に該当します。要件はシンプルで、アクセシブルな名前に可視ラベルのテキストが含まれていることです。

<!-- ❌ 「送信」が含まれない -->
<button aria-label="確定">送信</button>

<!-- ✅ 可視テキストを含んだうえで補足している -->
<button aria-label="お問い合わせを送信">送信</button>

<!-- ✅✅ そもそも aria-label が不要 -->
<button>お問い合わせを送信</button>

誤用③ 読み方を制御しようとする

一番気持ちはわかるが、一番やってはいけないパターンです。

<!-- ❌ 全部ダメ -->
<span aria-label="ワールドワイドウェブ">WWW</span>
<span aria-label="いちにちさんかい">1日3回</span>
<span aria-label="にせんにじゅうろくねんくがつじゅうにち">2026/9/10</span>

問題は3つあります。

  1. そもそも効かない(誤用①のとおり span には名前が付かない)
  2. 読み方は制御できない。スクリーンリーダーの読み上げは、名前の文字列を各エンジンの読み上げ規則で処理した結果です。ひらがなを渡しても意図した音になる保証はありません
  3. 点字ディスプレイと詳細読みを壊す。名前がひらがなに置き換わると、点字利用者には元の漢字が届かず、「詳細読み(漢字の説明読み)」で確認する手段も失われます

日付や単位の読み上げは、多くの場合スクリーンリーダー側が処理を持っています。開発者が先回りして上書きするより、マークアップで意味を伝えるだけにしておくほうが安全です。

<!-- ✅ 機械可読な値を持たせるだけ -->
<time datetime="2026-09-10">2026年9月10日</time>

どうしても読み分けが必要な固有名詞などは、aria-label ではなく可視テキストそのものを見直すのが正攻法です(「WWW」ではなく「World Wide Web(WWW)」と書く、など)。

誤用④ label / alt で済むところに使う

<!-- ❌ -->
<input type="text" aria-label="お名前">

<!-- ✅ 可視ラベルがあるほうが全員にとって良い -->
<label for="name">お名前</label>
<input id="name" type="text">

placeholder をラベル代わりにして aria-label で補うパターンもよく見ますが、placeholder は入力を始めると消えるので、晴眼者にとってもラベルが失われます。ラベルは可視で常時表示が原則です。

画像も同様に、alt が第一選択です。

<!-- ❌ -->
<img src="logo.svg" aria-label="SPOCALE">

<!-- ✅ -->
<img src="logo.svg" alt="SPOCALE">

誤用⑤ aria-labelledby との併用

<!-- ❌ aria-label は無視される -->
<button aria-labelledby="btn-text" aria-label="送信">
  <span id="btn-text">確定</span>
</button>

aria-labelledby が勝つので aria-label は死にコードです。レビューで見つけたら片方を消します。

なお aria-labelledby は複数IDを空白区切りで並べられ、その順に連結されます。ここは便利なので覚えておくとよいです。

<!-- 名前は「削除 2026年9月分のレポート」 -->
<button aria-labelledby="label-delete label-target">
  <svg aria-hidden="true"></svg>
</button>
<span id="label-delete" hidden>削除</span>
<span id="label-target">2026年9月分のレポート</span>

aria-labelledbyhidden な要素や display: none の要素も参照できます(aria-hidden の子孫でも参照可)。ここは aria-label にない強みです。


4. 判断フロー:aria-labelは最後に来る

名前を付けたいと思ったら、上から順に検討します。

  1. 可視テキストで表現できるか? → できるならそれが最善。全員に伝わる
  2. ネイティブの仕組みがあるか?<label>alt<caption><legend><figcaption>
  3. DOM内に名前にできるテキストがあるか?aria-labelledby で参照する
  4. 視覚的に隠したテキストで足りるか? → 視覚的非表示クラス(後述)
  5. ここまで全部無理か? → ようやく aria-label

そして最後に確認します:その要素のロールは名前を受け付けるか?(受け付けないなら、そもそもマークアップが間違っています)

視覚的非表示(visually hidden)

aria-label の代わりに使える、地味に最強の手段です。

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

display: nonevisibility: hidden では支援技術からも消えるので使えません。上記は「見えないがアクセシビリティツリーには存在する」状態を作るイディオムです。

同じ文言のリンクが並ぶケースでは、aria-label より素直に書けます。

<!-- △ aria-label 版:可視テキスト「詳しく見る」を含んでいるので 2.5.3 は満たすが、翻訳されない -->
<a href="/product-a" aria-label="製品Aについて詳しく見る">詳しく見る</a>

<!-- ✅ 視覚的非表示版:翻訳される、コピペできる、実装がシンプル -->
<a href="/product-a">詳しく見る<span class="visually-hidden">(製品A)</span></a>

ちなみに、そもそもリンクテキスト自体を具体的にできるなら、それが最善です。デザイン上のカードUIなら、見出しをリンクにするだけで解決することも多いです。


5. aria-labelは翻訳されない

見落とされがちな実務上の落とし穴です。ブラウザやサービスの機械翻訳は、多くの場合 aria-label の値を翻訳しません。テキストノードは翻訳されるのに、属性値は置き換わらない、という状態になります。

結果として、日本語サイトを英語で読んでいるスクリーンリーダーユーザーには、本文は英語、ボタンの名前だけ日本語という体験が起こります。

多言語対応やブラウザ翻訳が視野に入るプロダクトでは、これも「可視テキスト+視覚的非表示テキスト」を選ぶ理由になります。


6. 確認方法

書いたら必ず確認します。目視では絶対にわかりません。

Chrome DevTools

  1. Elements パネルで対象要素を選択
  2. 右側の Accessibility ペインを開く
  3. Computed PropertiesName を見る

Name の横に、どのソースから来た名前かが表示されます。aria-label が無視されている場合、Name が空だったり要素の内容になっていたりします。ここが「効いているか」の唯一の答え合わせです。

同じペインの Accessibility Tree で、divgeneric になっていることも確認できます。

自動チェック

  • axe DevTools / Lighthousearia-label の禁止ロールでの使用、名前の欠落などを検出
  • HTMLバリデータ:名前を付けられないロールへの ARIA 属性を警告するようになってきています
  • eslint-plugin-jsx-a11y:Reactなら CI に入れておく

ただし自動チェックで拾えるのは「明らかな間違い」までで、誤用②(可視テキストとの不一致)や誤用③(読み方の上書き)は人間のレビューでしか止まりません

実機

最後は実機です。日本語環境なら NVDA、PC-Talker、macOS/iOS の VoiceOver。まずは NVDA + Chrome で、Tabキーで一周してみるだけでも発見があります。


7. レビュー用チェックリスト

aria-label を見つけたら、この順で確認します。

  • その要素のロールは名前を受け付けるか(div/span/p/em/strong などになっていないか)
  • 可視テキストや <label> / alt で置き換えられないか
  • DOM内に参照できるテキストがあり、aria-labelledby にできないか
  • 可視テキストがある場合、その文字列が aria-label の値に含まれているか(WCAG 2.5.3)
  • 名前にロール名(「ボタン」「リンク」「ナビゲーション」)を含めていないか
  • 読み方の調整を目的にしていないか
  • aria-labelledby と併記していないか
  • 多言語・ブラウザ翻訳を考慮すべきプロダクトで、翻訳されないことを許容できるか
  • DevTools の Accessibility ペインで Name を確認したか

おわりに

aria-label は「アクセシビリティ対応を足す」属性ではなく、ネイティブの名前を上書きして奪う属性です。強い属性なので、効く場所は限られ、間違えたときの副作用は大きい。

そして重要なのは、aria-label が必要になった時点で「マークアップかデザインに、名前の出しどころがない」というサインが出ているということです。アイコンボタンのように本当に出しどころがないケースもありますが、多くは可視テキストを見直せば aria-label ごと消えます

「ARIAを足す前に、HTMLを直せるか考える」— ARIAを扱うときの原則は、結局これに尽きます。


参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?