セマンティック HTML

a11y の 9 割はここで決まる。適切なタグを使うと、role / name / state / value がブラウザによって自動で組み立てられる。 逆に divspan だけで作ると、それを ARIA で全部埋め直すことになる。

ドキュメントの基本

<html lang> は必須

スクリーンリーダの読み上げ言語、ブラウザの翻訳機能、ハイフネーションなど多くの機能が依存する。

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>ページの目的を表す簡潔なタイトル</title>
  </head>
  ...
</html>

ページ内に別言語の塊があるならそこに lang を:

<p>以下は英語の引用です:
  <span lang="en">The quick brown fox.</span>
</p>

<title> はページの目的を簡潔に

ランドマーク要素

ページの大きな構造を示すタグ。スクリーンリーダ利用者はランドマーク間をジャンプして目的の領域に素早く移動する。

要素役割暗黙の role
<header>ページ全体のヘッダ(最上位の場合)banner
<nav>主要なナビゲーションnavigation
<main>そのページの主要コンテンツmain
<aside>本文を補足する内容complementary
<footer>ページ全体のフッタ(最上位の場合)contentinfo
<section> + 見出し章・節(見出しがあると region になる)region(見出し付きの場合)
<article>独立して再配布可能な単位article
<form> + ラベルフォーム(aria-label/labelledby が必要)form
基本のページ骨格
<body>
  <header>
    <a href="/" aria-label="サイトトップへ">ロゴ</a>
    <nav aria-label="グローバル">
      <ul>
        <li><a href="/about">About</a></li>
        <li><a href="/blog">Blog</a></li>
      </ul>
    </nav>
  </header>
  <main>
    <h1>ページタイトル</h1>
    ...
  </main>
  <aside aria-label="関連リンク">
    ...
  </aside>
  <footer>
    <small>© 2026 ...</small>
  </footer>
</body>

同じランドマークが複数あるとき

ページ内に <nav> が複数あるなら、それぞれに aria-label を付けて識別する。 スクリーンリーダのランドマーク一覧で「ナビゲーション、ナビゲーション、ナビゲーション」では区別できない。

<nav aria-label="グローバル">...</nav>
<nav aria-label="目次">...</nav>
<nav aria-label="パンくず">...</nav>

見出し階層

h1h6見た目の大きさではなく論理的な階層。 スクリーンリーダ利用者は見出しジャンプでページの構造を把握する。

良い階層
<h1>React の基礎</h1>
  <h2>コンポーネント</h2>
    <h3>props</h3>
    <h3>state</h3>
  <h2>フック</h2>
    <h3>useState</h3>

ボタンとリンクの使い分け

最頻出の混同。遷移するなら <a>動作を起こすなら <button>

<a href><button>
キーボードEnter で発火Enter / Space で発火
右クリックメニュー「新しいタブで開く」等が出る出ない
URL を持つはいいいえ
用途場所を移動するその場で何かを起こす(送信、開閉、削除)
アンチパターン
<!-- BAD: div でボタン -->
<div class="btn" onclick="submit()">送信</div>

<!-- BAD: ハッシュリンクをボタン代わりに -->
<a href="#" onclick="event.preventDefault(); openModal()">開く</a>

<!-- GOOD -->
<button type="button" onclick="openModal()">開く</button>

<button type="..."> を明示

フォームの中の <button>暗黙で type="submit"。 意図せず submit が走るのを避けるため、常に type を書く

<button type="submit">送信</button>
<button type="button">キャンセル</button>
<button type="reset">リセット</button>

リスト

並列の項目はリスト要素で。スクリーンリーダは「3項目のリスト」のように個数を読み上げる。

ナビゲーションは <ul> でリスト化するのが定番。 要素数が伝わって便利。

テーブル

本物のテーブルデータには <table> を使う。レイアウトに使わない

<table>
  <caption>月別売上</caption>
  <thead>
    <tr>
      <th scope="col">月</th>
      <th scope="col">売上</th>
      <th scope="col">前年比</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">1月</th>
      <td>100,000</td>
      <td>+10%</td>
    </tr>
  </tbody>
</table>

強調・引用

タグ意味
<strong>強い重要性。スクリーンリーダで強調されることがある。
<em>言語的な強調(イントネーションの変化)。
<b>意味を伴わない太字(製品名・キーワード)。
<i>意味を伴わない斜体(学名・他言語)。
<mark>マーカーで引いた感じ。検索結果のハイライトに。
<q>インラインの引用。
<blockquote cite="...">ブロック引用。cite で出典 URL。
<cite>作品名の参照。
<abbr title="...">略語。title で展開した形。

コンテンツ系

<dialog>

モーダル/非モーダルのダイアログをネイティブで提供。showModal() で開くと フォーカスのトラップやバックドロップ、Esc キーでの閉じ込みも自動で動く。

<dialog id="confirm">
  <form method="dialog">
    <p>この操作を実行しますか?</p>
    <button value="cancel">キャンセル</button>
    <button value="ok">OK</button>
  </form>
</dialog>
<script>
  document.getElementById("confirm").showModal()
</script>

<details> / <summary>

ネイティブの折りたたみ。アコーディオンを自分で実装する前にこれで足りないか考える。

<details>
  <summary>詳細を見る</summary>
  <p>隠れていたコンテンツ</p>
</details>

<progress> / <meter>

<time datetime>

日時は機械可読な形で。datetime 属性で正規化。

<time datetime="2026-05-10">2026年5月10日</time>

避けるべきアンチパターン

迷ったら

そのコンテンツが意味的に何かを考える。
- 場所を移動する→<a>
- 動作する→<button>
- 構造を表す→<header> / <nav> / <main> / <article> / <section>
- 並列項目→<ul>
- データの表→<table>
意味の無い「何か」を入れたい時にだけ <div> / <span>