nav で作るナビゲーションバー
メニューを <nav> で囲み、リンクをリストに入れます。flexbox でリストを横並びのバーにします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Navbar</title>
<style>
body { margin: 0; font-family: system-ui, sans-serif; }
nav { background: #111827; }
nav ul { display: flex; gap: 4px; list-style: none; margin: 0; padding: 8px; }
nav a {
display: block; padding: 8px 14px; border-radius: 6px;
color: #e5e7eb; text-decoration: none;
}
nav a:hover { background: #374151; color: #fff; }
</style>
</head>
<body>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Courses</a></li>
<li><a href="#">Pricing</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
</body>
</html>
list-style: none と padding: 0 で行頭記号とインデントを消しています。各リンクに display: block を指定すると padding がバーの中で実際の場所を取るので、padding を含む箱全体がクリックできる範囲になります。display: flex の行を消すと、同じメニューがサイドバーのメニューのような縦のリストになります。
<nav> 自体には見た目がありません。その役割は意味にあります。スクリーンリーダーは nav を navigation ランドマークとして扱うので、ユーザーはキー 1 つでメニューに移動したり、メニューを飛ばしたりできます。
現在のページを示す
表示中のページへのリンクに aria-current="page" を付けます。スクリーンリーダーに「ここにいます」と伝え、CSS でリンクを強調するための手がかりにもなります。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Current page</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
nav ul { display: flex; gap: 20px; list-style: none; padding: 0; margin: 0; border-bottom: 2px solid #e5e7eb; }
nav a { display: block; padding: 10px 0; color: #374151; text-decoration: none; }
nav a[aria-current="page"] {
color: #2563eb; font-weight: 600;
box-shadow: inset 0 -3px 0 #2563eb;
}
</style>
</head>
<body>
<nav aria-label="Main">
<ul>
<li><a href="#">Overview</a></li>
<li><a href="#" aria-current="page">Lessons</a></li>
<li><a href="#">Projects</a></li>
</ul>
</nav>
</body>
</html>
aria-current="page" を別のリンクに移すと、強調もそれについていきます。.active クラスではなくこの属性にスタイルを付ければ、見た目とアクセシビリティが常に一致します。片方だけになることはありません。
1 ページに複数の nav: ラベルとパンくずリスト
1 つのページにメインメニュー、パンくずリスト、目次があり、それぞれが独自の <nav> に入ることがあります。それぞれに aria-label を付ければ、スクリーンリーダーは同じ「navigation」を 3 つ並べる代わりに「Breadcrumb navigation」のように読み上げます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Breadcrumb</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
.crumbs ol { display: flex; flex-wrap: wrap; list-style: none; padding: 0; margin: 0; }
.crumbs li + li::before { content: "/"; margin: 0 8px; color: #9ca3af; }
.crumbs a { color: #2563eb; }
</style>
</head>
<body>
<nav class="crumbs" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li><a href="#">Docs</a></li>
<li><a href="#">HTML</a></li>
<li><span aria-current="page">nav</span></li>
</ol>
</nav>
<h1>HTML nav</h1>
</body>
</html>
パンくずリストは順番に意味があるので、番号付きリストの <ol> を使います。スラッシュは CSS の ::before で付けているので、スクリーンリーダーが項目ごとに「スラッシュ」と読み上げることはありません。最後の項目は現在のページなので、リンクにしていません。
目次
同じページの各セクションへのリンクのリストもナビゲーションです。#id のリンクはその id を持つ要素に移動します。プレビューで試してみてください。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Table of contents</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
nav { border: 1px solid #e5e7eb; border-radius: 8px; padding: 8px 16px; }
nav h2 { font-size: 14px; margin: 4px 0; }
section { min-height: 200px; border-top: 1px solid #e5e7eb; }
</style>
</head>
<body>
<nav aria-labelledby="toc-title">
<h2 id="toc-title">On this page</h2>
<ol>
<li><a href="#install">Install</a></li>
<li><a href="#configure">Configure</a></li>
<li><a href="#deploy">Deploy</a></li>
</ol>
</nav>
<section id="install"><h2>Install</h2></section>
<section id="configure"><h2>Configure</h2></section>
<section id="deploy"><h2>Deploy</h2></section>
</body>
</html>
ここではラベルを aria-labelledby で見える見出しから取っています。見出しがすでに画面にあるなら、aria-label よりこちらのほうが適しています。
モバイルメニューの開閉ボタン
狭い画面では、メニューはたいていボタンの後ろに隠れています。aria-expanded を付けた本物の <button> を使い、リンクは <nav> の中に置いたままにします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Menu toggle</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
button { font: inherit; padding: 6px 12px; border: 1px solid #111827; border-radius: 6px; background: #fff; cursor: pointer; }
#menu { list-style: none; padding: 0; margin: 8px 0 0; border: 1px solid #e5e7eb; border-radius: 8px; }
#menu a { display: block; padding: 10px 14px; color: #111827; text-decoration: none; }
#menu li + li { border-top: 1px solid #e5e7eb; }
</style>
</head>
<body>
<nav aria-label="Main">
<button aria-expanded="false" aria-controls="menu" id="toggle">Menu</button>
<ul id="menu" hidden>
<li><a href="#">Home</a></li>
<li><a href="#">Courses</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<script>
const toggle = document.getElementById('toggle');
const menu = document.getElementById('menu');
toggle.addEventListener('click', () => {
const open = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', String(!open));
menu.hidden = open;
});
</script>
</body>
</html>
aria-expanded はメニューが開いているかどうかをスクリーンリーダーに伝え、hidden 属性は閉じている間、リストを画面からもアクセシビリティツリーからも取り除きます。広い画面では、メディアクエリでリストを表示してボタンを隠します。
よくある間違い
- リンクのまとまりをすべて
<nav> で囲む。 メインメニュー、パンくずリスト、ページ送り、目次に使いましょう。本文中のリンクはナビゲーションのまとまりではありません。
<div> で作ったメニューボタン。 キーボードでフォーカスも押すこともできません。<button> を使いましょう。
- ラベルのない複数の nav。 区別できるよう、それぞれに
aria-label か aria-labelledby を付けましょう。
- 現在のリンクに色を付けるだけ。
aria-current="page" を付けましょう。色だけでは読み上げられず、すべての人に見えるとも限りません。
- 本番で
href="#" のリンクを使う。 デモなら問題ありませんが、新しいタブで開いても動くよう、実際のメニュー項目には本当の URL が必要です。href の値の仕組みは HTML のリンク のページで解説しています。