はじめに
自分はコードレビューで「なぜこのタグを使いましたか?」「ここは~タグの方がよいと思います」という指摘を受けることが多く、タグに意味を持ってコーディングしていないことが多かったです。
そこで本記事では、アンチパターンだらけのHTMLを段階的にセマンティックなマークアップへ修正し、Lighthouseのアクセシビリティスコアがどう変化するかを検証してみました。
検証の目的と背景
- 目的: 「なぜこのタグを使うべきか」という判断基準をデータ(Lighthouseのスコア)を用いて確認し、コード品質を向上する
- 検証ツール: Google Chrome Lighthouse (Accessibilityスコア)
- 検証用のベースHTML: ヘッダー、ナビゲーション、メイン(見出しとリスト)、フッター、モーダルを開くボタンなどを備えた一般的なページ構造を想定。
検証の進め方
1.ベースHTMLを用意する
まず、検証のスタート地点として、ダメな例を詰め込んだ1枚のHTMLを作成しました。見た目は普通のブログなんですが、中身はアンチパターンだらけの状態になっています。

(ベースのHTML,CSSはAIに書いてもらいました。一瞬でこの完成度を出されるのはAIの進化を感じますね)
テスト用HTMLのコードを見る(クリックで展開)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>テストページ</title>
<link rel="stylesheet" href="test.css" />
</head>
<body>
<!-- ❌ 検証1: すべて div(ランドマーク要素なし) -->
<div class="header">
<h4>Tech Blog</h4>
<div>フロントエンド開発の知見を共有するブログ</div>
</div>
<!-- ❌ 検証1: nav ではなく div / 検証4: リストではなく div の羅列 -->
<div class="nav">
<div class="nav-link">ホーム</div>
<div class="nav-link">記事一覧</div>
<div class="nav-link">カテゴリ</div>
<div class="nav-link">お問い合わせ</div>
</div>
<div class="page-wrapper">
<!-- ❌ 検証1: main ではなく div -->
<div class="main-content">
<!-- ❌ 検証2: h1 がないのに h3 から始まる -->
<div class="card">
<h3>最新の記事</h3>
<div>フロントエンド開発に関する最新記事をお届けします。</div>
</div>
<!-- ❌ 検証2: h3 の次に h5(h4 をスキップ) -->
<div class="card">
<h5>セマンティックHTMLの重要性</h5>
<div>2026年3月17日</div>
<div>
適切なHTMLタグを選択することは、アクセシビリティの向上だけでなく、
SEOやコードの保守性にも大きく貢献します。本記事では、よくあるアンチパターンと
その改善方法について解説します。
</div>
<!-- ❌ 検証4: タグ一覧を div で並べている -->
<div style="margin-top: 16px;">
<span class="tag">HTML</span>
<span class="tag">アクセシビリティ</span>
<span class="tag">Lighthouse</span>
</div>
<!-- ❌ 検証3: ページ遷移しないアクションに a タグ + href="#" -->
<div style="margin-top: 16px;">
<a href="#" class="btn-modal" onclick="openModal()">詳細を見る</a>
</div>
</div>
<div class="card">
<h5>CSSグリッドレイアウト入門</h5>
<div>2026年3月10日</div>
<div>
CSS Grid
を使ったモダンなレイアウト手法を、実践的なコード例とともに紹介します。
Flexbox との使い分けについても解説します。
</div>
<div style="margin-top: 16px;">
<span class="tag">CSS</span>
<span class="tag">レイアウト</span>
</div>
<div style="margin-top: 16px;">
<a href="#" class="btn-modal" onclick="openModal()">詳細を見る</a>
</div>
</div>
<!-- ❌ 検証3: div にクリックイベント / フォームの label なし -->
<div class="card">
<h5>お問い合わせ</h5>
<div class="form-row">
<div class="label-text">お名前</div>
<input type="text" placeholder="山田 太郎" />
</div>
<div class="form-row">
<div class="label-text">メールアドレス</div>
<input type="email" placeholder="example@mail.com" />
</div>
<div class="form-row">
<div class="label-text">お問い合わせ内容</div>
<input type="text" placeholder="内容を入力してください" />
</div>
<div class="btn-submit" onclick="submitForm()">送信する</div>
</div>
</div>
<!-- ❌ 検証1: aside ではなく div -->
<div class="sidebar">
<div class="sidebar-box">
<h6>カテゴリ</h6>
<!-- ❌ 検証4: リスト項目を div で並べている -->
<div class="item">HTML / CSS</div>
<div class="item">JavaScript</div>
<div class="item">TypeScript</div>
<div class="item">React</div>
<div class="item">アクセシビリティ</div>
</div>
<div class="sidebar-box">
<h6>人気の記事</h6>
<!-- ❌ 検証4: ランキングなのに ol を使わず div -->
<div class="item">1. Flexbox 完全ガイド</div>
<div class="item">2. TypeScript 入門</div>
<div class="item">3. React Hooks まとめ</div>
</div>
<div class="sidebar-box">
<h6>アーカイブ</h6>
<div class="item">2026年3月</div>
<div class="item">2026年2月</div>
<div class="item">2026年1月</div>
</div>
</div>
</div>
<!-- ❌ 検証1: footer ではなく div -->
<div class="footer">
<div>© 2026 Tech Blog. All rights reserved.</div>
</div>
<!-- モーダル(❌ dialog 要素を使わず div で実装) -->
<div class="modal-overlay" id="modal-overlay">
<div class="modal-box">
<h5>記事の詳細</h5>
<div style="margin: 16px 0;">
この記事の詳細内容がここに表示されます。セマンティックHTMLを学んで、より良いWebサイトを作りましょう。
</div>
<div class="btn-submit" onclick="closeModal()">閉じる</div>
</div>
</div>
<script>
function openModal() {
document.getElementById("modal-overlay").classList.add("active");
}
function closeModal() {
document.getElementById("modal-overlay").classList.remove("active");
}
</script>
</body>
</html>
このコードには、以下のアンチパターンが含まれています。
| # | アンチパターン | 具体例 |
|---|---|---|
| 1 | すべて div で囲んでいる(ランドマーク要素なし) |
<div class="header">, <div class="footer">
|
| 2 | 見出し階層が破綻している |
h1 がなく h4 → h3 → h5 → h6
|
| 3 | ボタンに a タグや div を使っている |
<a href="#" onclick="...">, <div onclick="...">
|
| 4 | リスト項目を div で並べている |
<div class="item"> の羅列 |
| 5 | フォームに label がない |
<div class="label-text"> で代用 |
| 6 |
html 要素に lang 属性がない |
<html> のみ |
2. Lighthouse での計測方法
各検証ステップごとに、以下の手順でスコアを計測します。
- HTMLファイルをChromeで開く
- DevTools を開く(
F12またはCtrl + Shift + I) - 「Lighthouse」 タブを選択
- カテゴリで 「ユーザー補助(Accessibility)」 にチェックを入れる
- 「Analyze page load」 をクリックして計測
⚠️ 注意: 今回の検証はあくまでアクセシビリティの向上を目的としています。Lighthouse にはパフォーマンスや SEO など複数のカテゴリがありますが、本記事ではパフォーマンスの最適化ではなく、「ユーザー補助(Accessibility)」のスコアに注目して計測・比較を行います。
3. 検証の流れ
ベースのHTMLに対して、1つのカテゴリだけを修正 → 計測 を繰り返します。これにより、どの修正がどれだけスコアに影響するかを個別に確認できます。
ベースHTML
│
├─ 検証1: ランドマーク要素の導入 → 計測
│
├─ 検証2: 見出し階層の修正 → 計測
│
├─ 検証3: インタラクティブ要素の修正 → 計測
│
├─ 検証4: リスト構造の適正化 → 計測
│
├─ 検証5: lang 属性の追加 → 計測
│
└─ ベースHTMLとすべて適用後のスコアを比較
それでは、各カテゴリごとに検証していきます。
ベースHTMLの初期スコア
まず、アンチパターンをすべて含んだ状態のベースHTMLで Lighthouse を実行し、初期スコアを確認します。
-
Accessibility スコア: 72点
(jsなどを組み込んでいないためか、正直思ったより高かったです)
Lighthouse が検出した主な警告:
| 警告メッセージ | 内容 |
|---|---|
| 要素に [lang] 属性が指定されていまん | ページで lang 属性が指定されていない場合、スクリーン リーダーは、スクリーン リーダーの設定時にユーザーが選択したデフォルト言語がページで使用されているものと見なします。 |
| 見出し要素は降順になっていません | 見出しを適切なレベルの順序で配置すると、ページのセマンティック構造を伝えることができ、支援技術を使用した操作やコンテンツの把握が簡単になります。 |
| ドキュメントにメインのランドマークが設定されていません。 | メインのランドマークが 1 つ設定されていると、スクリーン リーダーのユーザーがウェブページを移動しやすくなります。 |
この状態をスタート地点として、ここから1つずつ改善していきます。
【検証1】ランドマーク要素の導入
まずは、ページの大枠となるレイアウト構造に対する検証です。
❌ Before(初期状態:オール div)
<div class="header">
<h4>Tech Blog</h4>
<div>フロントエンド開発の知見を共有するブログ</div>
</div>
<div class="nav">
<div class="nav-link">ホーム</div>
<div class="nav-link">記事一覧</div>
<div class="nav-link">カテゴリ</div>
<div class="nav-link">お問い合わせ</div>
</div>
<div class="page-wrapper">
<div class="main-content">
<!-- 記事カード等 -->
</div>
<div class="sidebar">
<!-- サイドバー -->
</div>
</div>
<div class="footer">
<div>© 2026 Tech Blog. All rights reserved.</div>
</div>
すべてが div で囲まれており、クラス名でしか役割がわかりません。
- Lighthouseの指摘: ドキュメントにメインのランドマークが設定されていません
⭕ After(セマンティックタグへの置換)
<header class="header">
<h4>Tech Blog</h4>
<div>フロントエンド開発の知見を共有するブログ</div>
</header>
<nav class="nav">
<div class="nav-link">ホーム</div>
<div class="nav-link">記事一覧</div>
<div class="nav-link">カテゴリ</div>
<div class="nav-link">お問い合わせ</div>
</nav>
<div class="page-wrapper">
<main class="main-content">
<!-- 記事カード等 -->
</main>
<aside class="sidebar">
<!-- サイドバー -->
</aside>
</div>
<footer class="footer">
<div>© 2026 Tech Blog. All rights reserved.</div>
</footer>
-
Lighthouseスコア変化: 72点 → 76点 (+4pt)
ページ全体の大きな構造には必ずランドマーク要素(<header>,<main>,<footer>,<nav>,<aside>)を使用します。各ランドマークは、支援技術を利用しているユーザーへ、配置・間隔・色・枠線の視覚的に伝えられるページ構造の特徴の始まりと終わりを知覚させることができます。
(https://qiita.com/degudegu2510/items/8aaf8466af00537e849a)
【検証2】見出しレベル(h1~h6)の不整合の解消
次に、見出しタグの使い方です。バラバラになっているhタグを検証します。
❌ Before(不整合な見出し)
<!-- h1 が存在せず h4 から始まっている -->
<div class="header">
<h4>Tech Blog</h4>
</div>
<!-- h4 の次が h3(逆行している) -->
<div class="card">
<h3>最新の記事</h3>
</div>
<!-- h3 の次に h5(h4 をスキップ) -->
<div class="card">
<h5>セマンティックHTMLの重要性</h5>
</div>
<div class="card">
<h5>CSSグリッドレイアウト入門</h5>
</div>
<div class="card">
<h5>お問い合わせ</h5>
</div>
<!-- サイドバーで突然 h6 -->
<div class="sidebar-box">
<h6>カテゴリ</h6>
</div>
<div class="sidebar-box">
<h6>人気の記事</h6>
</div>
<div class="sidebar-box">
<h6>アーカイブ</h6>
</div>
- Lighthouseの指摘: 見出し要素は降順になっていません
⭕ After(文書構造に沿った見出し)
<!-- h1 をページのメインタイトルに -->
<header class="header">
<h1>Tech Blog</h1>
</header>
<!-- h2 でセクションの見出し -->
<div class="card">
<h2>最新の記事</h2>
</div>
<!-- h3 で各記事タイトル -->
<div class="card">
<h3>セマンティックHTMLの重要性</h3>
</div>
<div class="card">
<h3>CSSグリッドレイアウト入門</h3>
</div>
<div class="card">
<h3>お問い合わせ</h3>
</div>
<!-- サイドバーも h2 で統一 -->
<div class="sidebar-box">
<h2>カテゴリ</h2>
</div>
<div class="sidebar-box">
<h2>人気の記事</h2>
</div>
<div class="sidebar-box">
<h2>アーカイブ</h2>
</div>
-
Lighthouseスコア変化: 76点 → 80点 (+4pt)
適切にhタグを設定することで、ページの構造が視覚的にも論理的にも整理され、ユーザーや検索エンジンにとって分かりやすいページを作成できます。デザイン調整のためにhタグを使用することはNGです。
デザイン調整にはCSSを使い、hタグはあくまで見出しとしての役割に徹しましょう。
(https://qiita.com/dxo_hashimoto/items/69599593f454e0de87ef)
【検証3】インタラクティブ要素の適切な選択(a | button)
クリックできる要素をマークアップする際によくある間違いです。
❌ Before(aタグやdivタグの誤用)
<!-- ページ遷移しないアクションに a タグ + href="#" -->
<a href="#" class="btn-modal" onclick="openModal()">詳細を見る</a>
<!-- div にクリックイベント(キーボード操作不可) -->
<div class="btn-submit" onclick="submitForm()">送信する</div>
<!-- モーダルの閉じるボタンも div -->
<div class="btn-submit" onclick="closeModal()">閉じる</div>
<!-- フォームの label がない(div で代用) -->
<div class="form-row">
<div class="label-text">お名前</div>
<input type="text" placeholder="山田 太郎" />
</div>
<div class="form-row">
<div class="label-text">メールアドレス</div>
<input type="email" placeholder="example@mail.com" />
</div>
-
Lighthouseの指摘: この問題は Lighthouse では警告として検出されませんでした。 しかし実際には
<div>のボタンは Tab キーでフォーカスできず、スクリーンリーダーにも認識されません。
⭕ After(buttonタグへの統一・WAI-ARIAの付与・labelの紐付け)
<!-- ページ遷移しないアクション → button -->
<button
type="button"
class="btn-modal"
aria-expanded="false"
aria-controls="modal-overlay"
>
詳細を見る
</button>
<!-- フォーム送信 → button type="submit" -->
<button type="submit" class="btn-submit">送信する</button>
<!-- モーダルの閉じるボタン → button -->
<button type="button" class="btn-submit" onclick="closeModal()">閉じる</button>
<!-- label と input を for/id で紐付け -->
<div class="form-row">
<label for="name" class="label-text">お名前</label>
<input type="text" id="name" placeholder="山田 太郎" />
</div>
<div class="form-row">
<label for="email" class="label-text">メールアドレス</label>
<input type="email" id="email" placeholder="example@mail.com" />
</div>
-
Lighthouseスコア変化: 80点 → 88点 (+8pt)
-
<a>タグ: 別のページへの遷移、またはページ内の別要素へのスクロール。 -
<button>タグ: ページ遷移を伴わないアクション(モーダルの開閉、メニューのトグル、フォーム送信など)。
div要素にはアクセシビリティ機能がなく、
キーボードでのフォーカスは当たらず、スクリーンリーダでは認識ができません。
故に、キーボード操作可能でクリックしたい要素には、<button>と<a>を適用するべきです。
(https://qiita.com/kobayashimakoto/items/2bbdcaadc037e3fdcf11)
-
補足:Lighthouseでは検出されにくい問題
インタラクティブ要素は、Lighthouseの自動テストでは警告が出にくいケースっぽいです。
<a href="#">にテキストがあれば「リンクに名前がある」として合格する(「本当にリンクであるべきか?」までは判定しない)<div onclick="...">は Lighthouse からはただのdivに見えるため、インタラクティブ要素として検査対象にならない<input>にplaceholderがあると、それがアクセシブルな名前として認識され、<label>がなくても合格扱いになることがある
【検証4】リスト構造の適正化(ul/liの正しい使用)
複数の要素が並んでいる箇所に対するマークアップです。
❌ Before(リストなのに div の羅列)
<!-- ナビゲーションリンクが div の羅列 -->
<div class="nav">
<div class="nav-link">ホーム</div>
<div class="nav-link">記事一覧</div>
<div class="nav-link">カテゴリ</div>
<div class="nav-link">お問い合わせ</div>
</div>
<!-- カテゴリ一覧が div の羅列 -->
<div class="sidebar-box">
<h6>カテゴリ</h6>
<div class="item">HTML / CSS</div>
<div class="item">JavaScript</div>
<div class="item">TypeScript</div>
<div class="item">React</div>
<div class="item">アクセシビリティ</div>
</div>
<!-- ランキングなのに ol を使わず div -->
<div class="sidebar-box">
<h6>人気の記事</h6>
<div class="item">1. Flexbox 完全ガイド</div>
<div class="item">2. TypeScript 入門</div>
<div class="item">3. React Hooks まとめ</div>
</div>
⭕ After(ul/ol/liによる構造化)
<!-- ナビゲーションリンクを ul/li で構造化 -->
<nav class="nav">
<ul>
<li class="nav-link">ホーム</li>
<li class="nav-link">記事一覧</li>
<li class="nav-link">カテゴリ</li>
<li class="nav-link">お問い合わせ</li>
</ul>
</nav>
<!-- カテゴリ一覧を ul/li で構造化 -->
<div class="sidebar-box">
<h2>カテゴリ</h2>
<ul>
<li class="item">HTML / CSS</li>
<li class="item">JavaScript</li>
<li class="item">TypeScript</li>
<li class="item">React</li>
<li class="item">アクセシビリティ</li>
</ul>
</div>
<!-- ランキングは順序ありの ol/li -->
<div class="sidebar-box">
<h2>人気の記事</h2>
<ol>
<li class="item">Flexbox 完全ガイド</li>
<li class="item">TypeScript 入門</li>
<li class="item">React Hooks まとめ</li>
</ol>
</div>
-
Lighthouseスコア変化: 88点 → 89点 (+1pt)
項目が順序なし(<ul>)または順序あり(<ol>)の「集合・リスト」である場合は、専用のタグを使います。スクリーンリーダーは「リスト、5項目」のように読み上げてくれるため、ユーザーが情報量を事前に把握できるようになります。ランキングのように順番に意味がある場合は<ol>を使用します。
【検証5】<html> 要素への lang 属性の追加
Lighthouse の警告に <html> 要素に [lang] 属性が指定されていません と出ていました。これはセマンティックHTMLのタグ選びとは少し異なりますが、アクセシビリティスコアに直接影響する項目です。
❌ Before(lang 属性なし)
<html>
スクリーンリーダーがページの言語を判定できず、日本語のページなのに英語の発音で読み上げてしまう可能性があります。
⭕ After(lang 属性を追加)
<html lang="ja">
-
Lighthouseスコア変化:89点 → 95点 (+6pt)
<html>要素には必ずlang属性を指定します。これによりスクリーンリーダーが正しい言語で読み上げ、ブラウザの翻訳機能も適切に動作するようになります。たった1属性ですが、スコアへの影響は大きかったです。
スコア推移の結果
上記のアンチパターンを是としたHTMLと、すべて修正したHTMLで以下の結果になりました。
アンチパターンHTMLの結果
- Accessibility スコア: 72点
- 特徴: アウトラインが破綻しており、キーボード操作でボタンが押せない箇所がある。
修正後HTMLの結果
- Accessibility スコア: 95点
- 特徴: DOMツリーが整理され、キーボード操作やスクリーンリーダーでの読み上げが正しく機能する。
今回は使用しなかったがアクセシビリティ向上に関与しそうな書き方
検証の中で調べていく中で、今回のテストページでは使う場面がなかったものの、効果がありそうな書き方をいくつか紹介します。
1. <table> の <caption> と scope 属性
データテーブルに <caption> でタイトルを、<th> に scope 属性でヘッダーの方向を明示すると、スクリーンリーダーが「行2、列:名前、値:山田太郎」のように読み上げてくれます。
<table>
<caption>
メンバー一覧
</caption>
<thead>
<tr>
<th scope="col">名前</th>
<th scope="col">役割</th>
</tr>
</thead>
<tbody>
<tr>
<td>山田 太郎</td>
<td>フロントエンド</td>
</tr>
</tbody>
</table>
2. <details> / <summary> によるアコーディオン
JavaScript なしでアコーディオンUIを実現できます。開閉状態はブラウザが管理するため、スクリーンリーダーにも「折りたたみ、展開済み」のように状態が伝わります。
<details>
<summary>よくある質問:送料はかかりますか?</summary>
<p>5,000円以上のご注文で送料無料です。</p>
</details>
3. aria-live で動的な変更を通知する
検索結果の件数やエラーメッセージなど、ページ遷移なしに内容が変わる領域は、スクリーンリーダーが変化に気づけません。aria-live を付けると、内容が変わったタイミングで自動的に読み上げてくれます。
<!-- 検索結果(操作の区切りで読み上げ) -->
<div aria-live="polite">
<p>検索結果: 12件</p>
</div>
<!-- エラー通知(即座に割り込んで読み上げ) -->
<div aria-live="assertive" role="alert">
<p>入力内容にエラーがあります</p>
</div>
なぜこのタグを使うべきかの判断基準まとめ
今回の検証を通して明確になった判断基準をまとめます。
-
レイアウトと領域:
divを避け、<main>や<section>、<article>などのランドマーク・セクションを使って骨格を作る。 -
見出し:
h1から順に階層構造(目次)を構築する。 -
インタラクション: ページを移動するなら
aタグ、その場で動くならbuttonタグ。 -
情報のグループ化: 単なる文字列の羅列ではなく、意味のある集合なら
ul/ol/liを使う。
今回の学び
今回の検証を通じて、以下のことを実感しました。
- タグを変えるだけでスコアが上がる: ランドマーク要素や見出し階層の修正といった書き換えるだけの対応でも、Accessibility スコアは着実に向上しました。
-
見た目上だけでは分からない問題がある:
div+onclickで見た目上は動作していても、キーボード操作やスクリーンリーダーでは使えない要素になっていました。 -
正しいタグ選びにはルールがある:「遷移なら
a、アクションならbutton」「集合ならul/ol」のように、明確な判断基準で選ぶことができます。
まとめ
今回は JS を使わず、簡単な HTML と CSS のみでの検証だったため改善の幅は限られていましたが、実際の現場ではもっと複雑かつ膨大なコードが書かれていると思います。その分、アクセシビリティ違反によってスコアが下がっているケースも多いはずです。
ぜひ皆さんもセマンティックなコーディングを心掛けて、自分のサイトや現場のコードを改善してみてください。
ここまで閲覧いただきありがとうございました!
参考資料:





