9/7まで 最大30%OFF 初心者編|無料コーディング教材|HTML・CSS・jQueryを実践で学ぶ

HTMLで目次を作る方法|ページ内リンクとデザイン3例を初心者向けに解説

HTMLの目次項目から対応する本文へ移動する様子を表した画像

「HTMLで目次を作りたいけれど、どのタグを使えばよいのか分からない」「目次のデザイン例も見てみたい」と困っていませんか?

HTMLの目次は、番号付きリストとページ内リンクを組み合わせて作ります。基本を覚えれば、ブログ記事や長い解説ページでも目的の見出しへすぐ移動できるようになります。

この記事では、「HTMLで目次を作る方法」「階層を深くする方法」「目次のデザイン3例」の順に解説します。実際の表示結果を確認しながら進めていきましょう。

HTMLで目次を作る方法

目次の2番目の項目から本文の2番目の見出しへ移動する関係を示した図
目次の項目をクリックすると、対応する本文の見出しへ移動します

基本的な目次の作り方

目次の項目には、番号付きリストを作るolタグ、項目を表すliタグ、リンクを作るaタグを使います。目次全体は「ページ内の案内」であることが伝わるように、navタグで囲みます。

リンクをクリックして見出しへ移動するには、aタグのhref="#id名"と、移動先の見出しに付けるid="id名"を一致させます。

移動先のidとhrefを一致させることで、aタグで作った目次をクリックすると対応する見出しへ移動できます。

olタグとliタグの基本は、次の記事で詳しく解説しています。

まずは、次のHTMLをindex.htmlへ書いてみてください。

<nav class="table-of-contents" aria-label="目次">
  <ol>
    <li><a href="#chapter1">目次1</a></li>
    <li><a href="#chapter2">目次2</a></li>
    <li><a href="#chapter3">目次3</a></li>
    <li><a href="#chapter4">目次4</a></li>
    <li><a href="#chapter5">目次5</a></li>
  </ol>
</nav>

<h2 id="chapter1">目次1の内容</h2>
<p>ここに本文を書きます。</p>

<h2 id="chapter2">目次2の内容</h2>
<p>ここに本文を書きます。</p>

<h2 id="chapter3">目次3の内容</h2>
<p>ここに本文を書きます。</p>

<h2 id="chapter4">目次4の内容</h2>
<p>ここに本文を書きます。</p>

<h2 id="chapter5">目次5の内容</h2>
<p>ここに本文を書きます。</p>

目次部分は次のように表示されます。

目次1から目次5までを並べた高解像度の基本的な番号付き目次
ol・li・aタグで作った基本的な目次

目次1をクリックすると、id="chapter1"を付けた見出しまでページ内を移動します。ほかの項目も、hrefとidが対応していれば同じように動きます。

リンクの色はcolorプロパティで変更できます。text-decoration: none;で下線を消すこともできますが、その場合も色やフォーカス表示を残し、リンクだと分かるデザインにしてください。

固定ヘッダーで見出しが隠れる場合

ページ上部に固定ヘッダーがあると、目次をクリックしたときに移動先の見出しがヘッダーの下へ隠れることがあります。

固定ヘッダーで隠れる見出しと80pxの余白を確保した見出しの比較図
scroll-margin-topを指定すると固定ヘッダーの下に余白を確保できます

この場合は、移動先の見出しへscroll-margin-topを指定します。ページ内リンクで移動したとき、見出しの上に指定した分の余白が確保されます。

h2[id],
h3[id] {
  scroll-margin-top: 80px;
}

上の例では、idを付けたh2・h3見出しの上に80pxの余白を確保しています。80pxの部分は、実際に使っている固定ヘッダーの高さに合わせて調整してください。

目次の階層を深くする方法

次に、目次を「2.1」「2.1.1」のような階層にする方法を説明します。h2の中にh3、h3の中にh4があるページなど、見出し同士の親子関係を表したいときに使います。

子階層のolタグは、親になるliタグの中へ入れます。見た目のためだけに階層を増やさず、本文の見出し構造とそろえることが大切です。

<nav class="toc-hierarchy" aria-label="目次">
  <ol>
    <li><a href="#chapter1">目次1</a></li>
    <li>
      <a href="#chapter2">目次2</a>
      <ol>
        <li>
          <a href="#chapter2-1">目次2.1</a>
          <ol>
            <li><a href="#chapter2-1-1">目次2.1.1</a></li>
            <li><a href="#chapter2-1-2">目次2.1.2</a></li>
          </ol>
        </li>
        <li><a href="#chapter2-2">目次2.2</a></li>
      </ol>
    </li>
    <li><a href="#chapter3">目次3</a></li>
    <li><a href="#chapter4">目次4</a></li>
    <li><a href="#chapter5">目次5</a></li>
  </ol>
</nav>

階層の番号はCSSカウンターで自動表示できます。目次以外のリストへ影響しないように、ここでは.toc-hierarchyの中だけを指定しています。

.toc-hierarchy ol {
  counter-reset: item;
  list-style: none;
  padding-left: 1.5rem;
}

.toc-hierarchy li {
  counter-increment: item;
  margin-block: 0.4rem;
}

.toc-hierarchy li::before {
  content: counters(item, ".") " ";
}

表示結果は次のとおりです。

2.1、2.1.1、2.1.2、2.2に対応する目次リンクを表示した階層目次
olタグを入れ子にし、2.xの階層番号と対応する目次リンクを表示した例

counter-resetは番号を数え始める準備、counter-incrementは項目ごとに番号を1つ進める指定です。counters()を使うことで、親と子の番号をピリオドでつないで表示できます。

目次のデザイン3選

青いボックス、01形式の番号、黄色い丸数字を使った目次デザイン3例
目次のデザイン3例を比較した画像

ここからは、目次のデザインを3つ見ていきます。

  1. 目次をボックスで囲む
  2. 数字を01の形式にして下線を引く
  3. 数字を丸で囲む

3例とも同じol・li・aタグを使い、主にCSSで見た目を変えています。HTMLの意味を保ったままデザインできる点を確認してください。

目次をボックスで囲む

最初は、目次全体を枠と背景色で囲む基本的なデザインです。本文との境界が分かりやすく、項目が多い目次にも使いやすい形です。

<nav class="toc-box" aria-label="目次">
  <ol>
    <li><a href="#chapter1">目次1</a></li>
    <li><a href="#chapter2">目次2</a></li>
    <li><a href="#chapter3">目次3</a></li>
    <li><a href="#chapter4">目次4</a></li>
    <li><a href="#chapter5">目次5</a></li>
  </ol>
</nav>
.toc-box {
  width: min(100%, 36rem);
  padding: 1.25rem 1.5rem;
  border: 1px solid #94a3b8;
  border-radius: 0.5rem;
  background-color: #eff6ff;
  box-sizing: border-box;
}

.toc-box ol {
  margin: 0;
  padding-left: 1.5rem;
}

.toc-box li + li {
  margin-top: 0.4rem;
}

表示させると次のようになります。

薄い青色の背景と枠で囲んだ高解像度の目次
背景色と枠を付けたボックス型の目次

元のコードにあった固定の高さは外し、項目が増えたり文字が折り返されたりしても枠が自然に広がるようにしています。width: min(100%, 36rem);により、スマートフォンでも画面幅からはみ出しません。

数字を01の形式にして下線を引く

次は、番号を「01」「02」の形式で表示し、番号の下へ線を引くデザインです。番号はHTMLへ一つずつ書かず、CSSカウンターで自動的に追加します。

01、02、03の番号へ青い下線を付けた高解像度の目次
2桁の番号と下線を組み合わせた目次
<nav class="toc-zero" aria-label="目次">
  <ol>
    <li><a href="#chapter1">目次1</a></li>
    <li><a href="#chapter2">目次2</a></li>
    <li><a href="#chapter3">目次3</a></li>
  </ol>
</nav>
.toc-zero ol {
  counter-reset: item;
  list-style: none;
  margin: 0;
  padding: 0;
}

.toc-zero li {
  counter-increment: item;
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 0.5rem;
}

.toc-zero li::before {
  content: counter(item, decimal-leading-zero);
  padding-bottom: 0.2rem;
  border-bottom: 2px solid #2563eb;
  font-size: 1.4rem;
}

decimal-leading-zeroは、一桁の番号の先頭へ0を付ける指定です。項目を追加しても番号を手作業で直す必要がないため、更新時の間違いを減らせます。

数字を丸で囲む

最後は、目次の番号を丸で囲むデザインです。こちらもCSSカウンターを使い、HTMLは通常のリストのままにします。

<nav class="toc-circle" aria-label="目次">
  <ol>
    <li><a href="#chapter1">目次1</a></li>
    <li><a href="#chapter2">目次2</a></li>
    <li><a href="#chapter3">目次3</a></li>
  </ol>
</nav>
.toc-circle ol {
  counter-reset: item;
  list-style: none;
  margin: 0;
  padding: 0;
}

.toc-circle li {
  counter-increment: item;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.5rem;
}

.toc-circle li::before {
  content: counter(item);
  display: grid;
  width: 2.25rem;
  aspect-ratio: 1;
  place-items: center;
  flex: 0 0 auto;
  border-radius: 50%;
  background-color: yellow;
}

表示結果は次のとおりです。

CSSのyellowで塗った丸の中に1、2、3を表示した目次
background-color: yellow;で番号を丸く囲んだ目次

widthaspect-ratio: 1;で縦横を同じ長さにし、border-radius: 50%;を指定すると円になります。display: grid;place-items: center;は、数字を円の中央へ配置するための指定です。

CSSを使わず、HTMLの文字参照で丸数字を表示する方法もあります。たとえば&#9312;は「①」、&#10102;は「❶」、&#9450;は「⓪」と表示されます。

白丸の1と黒丸の1を並べた高解像度の比較画像
白丸と黒丸の数字1を比較した画像

文字参照は手軽ですが、用意されている記号の範囲に限られます。色や大きさを自由に調整したい場合や、項目数が増える場合はCSSカウンターを使う方法が向いています。

丸数字の種類や入力方法は、次の記事でも紹介しています。

まとめ

HTMLの目次は、ol・li・aタグで項目を作り、リンクのhref="#id名"と見出しのid="id名"を対応させます。階層を増やす場合は、本文の見出し構造に合わせてolタグを入れ子にしてください。

基本の目次が動いたら、ボックス、01形式の番号、丸数字の順にCSSを試してみましょう。最後にすべてのリンクをクリックし、目的の見出しへ移動するか、スマートフォンで横にはみ出さないかを確認すれば完成です。