セマンティックHTML要素の使い分け
概要
セマンティックHTMLとは、HTML要素を「見た目」ではなく「意味」に基づいて使用するマークアップ手法である。HTML Living Standardでは、すべての要素と属性が「特定の意味(semantics)」を持つように定義されており、要素を本来の意図された目的で使用することを推奨している[1]。
セマンティックHTMLの利点
セマンティックHTML要素を使用することで、開発効率、チーム内コミュニケーション、マシンリーダービリティの3つの側面で利点が得られる。
開発効率の向上
ネイティブHTML要素には、必要な機能が最初から備わっている。一方、<div>などでUIを再実装すると、アクセシビリティ機能をすべて手動で実装する必要があり、開発効率が低下する。
<!-- NG: divでボタンを実装すると複雑なコードが必要 -->
<div tabindex="0" onclick="handleClick()" onkeydown="if(event.key==='Enter'||event.key===' ') handleClick()">
クリック
</div>
<!-- OK: button要素なら1行で済む -->
<button type="button" onclick="handleClick()">クリック</button>
コミュニケーションの改善
要素名自体が意図を表現する。<nav>なら「ナビゲーション」、<article>なら「記事コンテンツ」と即座に理解でき、コードレビューやメンテナンス時のコミュニケーションが改善される。
<!-- NG: divばかりで意図が不明瞭 -->
<div class="container">
<div class="nav-menu">...</div>
<div class="content">...</div>
</div>
<!-- OK: 要素名で意図が明確 -->
<div class="container">
<nav>...</nav>
<article>...</article>
</div>
マシンリーダービリティの向上
要素の意味が明確であるため、検索エンジンや支援技術が内容を正確に理解できる。これにより、SEO効果とアクセシビリティの両面で品質が向上する[2]。
<!-- NG: divでナビゲーションを実装 -->
<div class="navigation">
<a href="/">ホーム</a>
<a href="/about">会社概要</a>
</div>
<!-- OK: navでナビゲーションを明示 -->
<nav aria-label="メインナビゲーション">
<a href="/">ホーム</a>
<a href="/about">会社概要</a>
</nav>
要素タイプ別の使い分け
それでは、セマンティックHTMLの実装のために各要素をどのような場面で使用すべきかを役割ごとに説明する。
セクショニング要素
セクショニング要素(<article>, <section>, <aside>, <nav>)は、見出し階層には影響せず、コンテンツの関係性に基づいて選択する[3]。
<article>
RSSフィードで配信できるレベルの独立性を持つ、自己完結したコンテンツに使用する[4]。
<article>
<h2>MacBook Pro 14インチ M3 Max</h2>
<p>プロフェッショナルのための高性能ノートPC</p>
<dl>
<dt>価格</dt>
<dd>¥498,800</dd>
</dl>
<button type="button">カートに追加</button>
</article>
使用例:ブログ記事、ニュース記事、商品カード。
カードUIでは、単なるリンク(タイトルとサムネイルのみ)は自己完結コンテンツとは言えないため、<article>ではなく<li>や<div>の方が適切である。逆に、レビューコメント・ブログカード・商品詳細カードのように独立文書として成立する情報量がある場合は<article>を使用する。
<!-- NG: 単なるリンクカード(自己完結していない) -->
<article>
<a href="/products/123">
<img src="thumbnail.jpg" alt="" />
<h3>商品名</h3>
</a>
</article>
<!-- OK: リストアイテムとして扱う -->
<li>
<a href="/products/123">
<img src="thumbnail.jpg" alt="" />
<h3>商品名</h3>
</a>
</li>
<!-- OK: 独立文書として成立するカード -->
<article>
<h3>ユーザーレビュー</h3>
<p>この商品は期待以上の性能でした。特にバッテリー持続時間が素晴らしい。</p>
<footer>
<dl>
<dt>投稿者</dt>
<dd>山田太郎</dd>
</dl>
</footer>
</article>
<section>
見出しを伴うテーマ別のまとまりに使用する[5]。<section>には通常見出し(<h2>-<h6>)を持つべきである。<h1>はページ全体のタイトルとして1つのみ使用することが推奨されるため、セクション内では通常<h2>以下を使用する。
<section>
<h2>商品仕様</h2>
<dl>
<dt>CPU</dt>
<dd>Apple M3 Max</dd>
<dt>メモリ</dt>
<dd>32GB</dd>
</dl>
</section>
使用例:章、タブコンテンツ、記事内のセクション。
<aside>
間接的に関連するが、主要コンテンツから分離して考えることができる情報に使用する。
<aside>
<h2>関連商品</h2>
<ul>
<li>MacBook Air</li>
<li>iPad Pro</li>
</ul>
</aside>
使用例:サイドバー、関連リンク、広告。
<nav>
主要なリンクのまとまりに使用する。スクリーンリーダーは<nav>をランドマークとして認識するため、過剰に使用すると煩雑になる。サイト内移動やページ内移動など主要なものに限定する。
<nav aria-label="メインナビゲーション">
<ul>
<li><a href="/">ホーム</a></li>
<li><a href="/products">商品一覧</a></li>
<li><a href="/about">会社概要</a></li>
</ul>
</nav>
使用例:グローバルナビゲーション、パンくずリスト、ページ内目次。
テキスト構造とグルーピング
テキスト構造要素には意味を持つもの(<p>, <dl>)と、純粋なグループ化のためのもの(<div>, <span>)がある。
<p>
<!-- OK: 段落として意味のあるテキスト -->
<p>セマンティックHTMLは、要素を意味に基づいて使用するマークアップ手法である。</p>
<p>この手法により、アクセシビリティとSEOが向上する。</p>
<p>要素内には他の段落や見出しなどを含められない[6]。
<div>
意味を持たないコンテナ。CSSによるスタイリングやJavaScriptによる操作に使用する。
<!-- OK: レイアウトのためのグルーピング -->
<div class="card">
<h3>商品名</h3>
<p>商品説明</p>
</div>
意味的なまとまりには、<article>, <section>, <aside>などのセマンティック要素を優先する。<div>は、セマンティック要素が存在しない場合の最後の手段として使用する。
<span>
意味を持たない要素。テキストの一部にスタイルを適用する。
<!-- OK: テキストの一部にスタイル適用 -->
<p>価格: <span class="price">¥1,980</span></p>
<p><span class="highlight">重要</span>な情報です。</p>
<!-- NG: spanで意味的な強調 -->
<p><span class="bold">注意:</span>この操作は取り消せません。</p>
<!-- OK: strongで意味的な重要度を示す -->
<p><strong>注意:</strong>この操作は取り消せません。</p>
<dl> + <dt> + <dd>
名前(用語)と値(説明)のペアを表す説明リスト[7]。商品仕様、メタデータ、用語集などに使用する。
<!-- OK: 商品仕様 -->
<dl>
<dt>CPU</dt>
<dd>Apple M3 Max</dd>
<dt>メモリ</dt>
<dd>32GB</dd>
</dl>
<!-- NG: ulで無理やり表現 -->
<ul>
<li>CPU: Apple M3 Max</li>
<li>メモリ: 32GB</li>
</ul>
<!-- NG: tableの過剰使用 -->
<table>
<tr>
<td>CPU</td>
<td>Apple M3 Max</td>
</tr>
</table>
名前と値のペア(1対1または1対多の関係)は<dl>が適している。一方、行列的な表(複数項目の比較や交差データ)は<table>が適している。
テキスト意味付け要素
テキストの意味を明確に伝える要素として、<strong>(重要度)、<em>(強調)、<code>(コード)、<time>(日時)がある。
強調と重要度
<strong>
内容の重要性を示す。ネストにより、相対的な重要度を段階的に強められる[8]。
<p><strong>注意:</strong>この操作は取り消せません。</p>
<!-- ネストによる重要度の階層化 -->
<p><strong>警告:これは<strong>非常に危険</strong>です。</strong></p>
<em>
文の意味を変える強勢を示す。ネストにより、強調の度合いを段階的に強められる[8:1]。
<p>私は<em>昨日</em>それを見た。</p>
<p>
これは<em>本当に<em>重要</em></em>なポイントだ。
</p>
スクリーンリーダーによる強調・重要度の読み上げには実装差があるため、視覚以外での伝達には過信せず、文脈で意味を補強することが望ましい。
<code>
プログラミングコードやファイル名を表す。
<p><code>console.log()</code>でデバッグできる。</p>
<time>
日時や期間を機械可読な形式で表す[9]。datetime 属性で正確な日時を指定し、要素内にはユーザー向けの表示形式を記述する。
<!-- OK: 投稿日時 -->
<p>
投稿日:
<time datetime="2024-03-15T14:30:00+09:00">2024年3月15日 14:30</time>
</p>
<!-- OK: イベント期間 -->
<p>
開催期間:
<time datetime="2024-04-01">4月1日</time>
〜
<time datetime="2024-04-03">4月3日</time>
</p>
<!-- OK: 相対時間表記 -->
<p><time datetime="2024-03-15T14:30:00+09:00">2時間前</time>に更新</p>
datetime 属性により、検索エンジンやスクリーンリーダーに正確な日時情報を提供し、SEOやリッチスニペットに貢献する。
インタラクティブ要素
インタラクティブ要素は、操作の種類に基づいて選択する。状態変更には<button>、URL変更を伴う移動には<a>、折りたたみコンテンツには<details>を使用する。
<button> vs <a>
<button>
現在のページ内で状態を変更する。
<!-- OK: ページ内の状態変更 -->
<button type="button">カートに追加</button>
<button type="button">メニューを開く</button>
<button type="submit">フォームを送信</button>
<a>
別ページへの遷移、ページ内アンカー、外部サイトへのリンクに使用する。
<!-- OK: URL変更を伴う移動 -->
<a href="/products">商品一覧</a>
<a href="#specifications">仕様へ移動</a>
<a href="https://example.com">外部サイト</a>
余談だがhref属性のない<a>要素は、キーボードフォーカスが有効にならず、スクリーンリーダーにリンクとして認識されないのでアクセシビリティ上問題となる。
<details>
ユーザーが表示と非表示を切り替えられるコンテンツに使用する。
<details>
<summary>配送・返品について</summary>
<p>全国送料無料。商品到着後30日以内は返品可能です。</p>
</details>
<summary>は<details>の最初の子要素として1つだけ配置し[10]、見出し要素(<h1>-<h6>)を含めない[11]ようにする。理由としては、一部のスクリーンリーダーで見出しとして認識されず、見出しナビゲーション機能が使えなくなるためである。
<!-- NG: summary内に見出しを配置 -->
<details>
<summary><h3>配送情報</h3></summary>
<p>内容</p>
</details>
<!-- OK: シンプルなテキスト -->
<details>
<summary>配送情報</summary>
<p>内容</p>
</details>
iOS VoiceOverでは、<details>要素の展開/折りたたみ状態が正しく通知されない既知の問題がある[12]。重要な情報を<details>内に隠す場合は、代替手段の提供を検討する必要がある。
メディア要素
<figure> + <figcaption>
<figure>
画像、図表、コードスニペット、引用など、メインコンテンツから参照されるが、メインフローから分離できるコンテンツに使用する[13]。キャプションを伴う場合は <figcaption> を併用する。
<article>との違いは、<figure>の「自己完結性」が「独立配信可能」という意味ではない点である。本文で「図1を参照」のように言及されることが前提であり、本文と切り離しても意味が通る必要はない。重要なのは、メインフローから視覚的・構造的に分離できることである。
<!-- OK: 画像とキャプション -->
<figure>
<img src="/images/architecture.png" alt="システムアーキテクチャ図" />
<figcaption>図1: マイクロサービスアーキテクチャの全体構成</figcaption>
</figure>
<!-- OK: コードスニペットとキャプション -->
<figure>
<pre><code>
function isPrime(n) {
if (n <= 1) return false;
for (let i = 2; i * i <= n; i++) {
if (n % i === 0) return false;
}
return true;
}
</code></pre>
<figcaption>リスト1: 素数判定関数の実装</figcaption>
</figure>
<!-- OK: 引用とキャプション -->
<figure>
<blockquote>
<p>セマンティックHTMLは、要素を意味に基づいて使用するマークアップ手法である。</p>
</blockquote>
<figcaption>— HTML Living Standard より</figcaption>
</figure>
装飾的な画像、ロゴ、アイコンなど、キャプションが不要なものには使用しない。
<!-- NG: 装飾的な画像にfigureを使用 -->
<figure>
<img src="logo.png" alt="会社ロゴ" />
</figure>
<!-- OK: 単純なimg要素 -->
<img src="logo.png" alt="会社ロゴ" />
ロゴのalt属性は文脈によって異なる。純粋に装飾目的ならalt=""、ホームへのリンクならalt="サイト名"とする。
カードUI(商品カード、ブログ記事カードなど)では、商品名や記事タイトルが見出し要素(<h2>、<h3>など)としてすでに表示されている場合、画像に追加のキャプションは不要である。<figure>と<figcaption>で商品名を繰り返すと、意味の重複が発生する。
<!-- NG: ECサイトの商品カードで意味が重複 -->
<article class="product-card">
<figure>
<img src="/products/macbook-air.jpg" alt="MacBook Air" />
<figcaption>MacBook Air</figcaption>
</figure>
<h3>MacBook Air</h3>
<span class="price">¥148,800</span>
</article>
<!-- OK: 見出しと画像を分離 -->
<article class="product-card">
<img src="/products/macbook-air.jpg" alt="MacBook Air" class="product-image" />
<h3>MacBook Air</h3>
<span class="price">¥148,800</span>
</article>
<figcaption>
<figure> 要素内の最初または最後の子要素として配置する。図表番号、タイトル、説明、出典などを含める。
まとめ
セマンティックHTML要素は、見た目ではなく意味に基づいて選択する。独立文書なら<article>、テーマ別まとまりなら<section>、移動なら<a>、状態変更なら<button>のように、コンテンツの性質と操作の種類で判断する。この原則に従うことで、検索エンジンと支援技術が正しく解釈でき、開発効率とアクセシビリティが向上する。
-
WHATWG HTML - Replace the outline algorithm with one based on heading levels - 2022年7月1日、セクショニング要素ベースのアウトラインアルゴリズムを見出しレベル(h1-h6)ベースへ置き換え ↩︎
-
HTML Living Standard - article要素 - 独立して配布・再利用可能(例:syndication)な自己完結的なコンテンツを表す ↩︎
-
HTML Living Standard - p要素 - p要素のコンテンツモデルはフレージングコンテンツのみで、ブロックレベル要素を含められない ↩︎
-
HTML Living Standard - dl要素 - 名前と値のグループからなる説明リストを表す。用語と定義、メタデータなどに使用 ↩︎
-
HTML Living Standard - strong要素 / em要素 - 祖先要素としてのstrong/emの数がコンテンツの重要度・強調度を決定する ↩︎ ↩︎
-
HTML Living Standard - time要素 - 機械可読な形式で日時や期間を表す。datetime属性で正確な日時を指定 ↩︎
-
HTML Living Standard - details要素 - summary要素はdetails要素の最初の子要素として1つのみ配置可能 ↩︎
-
MDN Web Docs - summary要素 - summary要素内に見出し要素を配置すると、一部のスクリーンリーダー(NVDA、JAWS)でボタンロールが優先され、見出しとして認識されない ↩︎
-
GOV.UK Design System - Details component accessibility audit - iOS VoiceOverでdetails要素の展開/折りたたみ状態が正しく通知されない問題を報告(2024年監査)。重要情報をdetails内に配置する場合は代替手段の提供を推奨 ↩︎
-
HTML Living Standard - figure要素 - 図表、イラスト、写真、コードなど、メインコンテンツから参照されるが独立して理解できる自己完結的なコンテンツを表す ↩︎
Discussion