番号付きリストを入れ子にし、CSS の counters() を使って各階層を結合すると、1.2.1 のような完全なセクションパスを表示できます。このチュートリアルでは、Mac 上にローカルの HTML ファイルを作成し、アウトラインの順序を保ち、編集後に番号を確認する方法を説明します。

完全なパス番号はどのように機能しますか?

完全なセクションパスは、各階層における項目の位置を記録します。たとえば、1.2.1 は、最上位の最初の項目の2番目の子の中にある最初の項目を意味します。CSS には、ネストされたカウンター値を結合するための counters() が用意されています。最も外側のカウンターから最も内側のカウンターの順に読み取り、指定した区切り文字を挿入します。MDN: counters() CSS 関数

この作業では、アウトラインは枝分かれした入れ子のある番号付きリストです。文書内の別の場所にある見出しから自動生成される目次ではありません。HTML では番号付きリストの項目はツリー順に定義されるため、ソースの順序を意図する読み上げ順と一致させてください。HTML Standard: ol 要素

この例では、3つのルールを使います。対象となるすべてのリストにカウンターを作成し、各項目でカウンターを増やし、項目のラベルの前に結合したパスを表示します。これは MDN に記載された入れ子カウンターのパターンに従い、セレクターの対象をこのアウトラインに限定しています。MDN: CSS カウンターの使用

Mac でアウトラインファイルを作成する

必要なのは TextEdit と Safari などのブラウザーです。完全なサンプルにはスタイルシートと例示用の内容が含まれており、パッケージ、ビルドツール、JavaScript は必要ありません。

  1. TextEdit を開き、File > New を選択します。
  2. Format > Make Plain Text を選択します。
  3. 以下の完全なサンプルを貼り付けます。
  4. File > Save を選択し、ファイル名を outline.html にして、デスクトップなど使いやすい場所に保存します。
  5. TextEdit で使用する拡張子を確認されたら、Use .html を選択します。

Apple は、TextEdit で HTML ファイルを作成するこのプレーンテキストの手順を案内しています。Apple: Mac の TextEdit で HTML 書類を操作する

以下は完全な文書です。コードブロックの中身をコピーし、Markdown のフェンス行は含めないでください。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Nested outline example</title>
  <style>
    body {
      max-width: 48rem;
      margin: 2rem auto;
      padding: 0 1rem;
      font-family: system-ui, sans-serif;
      line-height: 1.6;
    }

    .outline,
    .outline ol {
      counter-reset: outline-section;
      list-style-type: none;
      padding-inline-start: 1.5rem;
    }

    .outline li {
      counter-increment: outline-section;
      margin-block: 0.35rem;
    }

    .outline li::before {
      content: counters(outline-section, ".") " ";
      font-weight: 600;
    }
  </style>
</head>
<body>
  <h1>Sample document outline</h1>
  <ol class="outline" role="list">
    <li>Node A
      <ol role="list">
        <li>Node B</li>
        <li>Node C
          <ol role="list">
            <li>Node D</li>
            <li>Node E</li>
          </ol>
        </li>
        <li>Node F</li>
      </ol>
    </li>
    <li>Node G
      <ol role="list">
        <li>Node H</li>
      </ol>
    </li>
    <li>Node I</li>
  </ol>
</body>
</html>

Safari を開き、Finder から outline.html を Safari のウインドウにドラッグします。TextEdit でソースを編集し、Safari で表示されたアウトラインを確認します。編集するたびにファイルを保存し、ブラウザーのページを再読み込みしてください。

TextEdit をコード編集モードに保つ

ファイルを開き直したとき、ソースコードではなく書式付きの内容が表示されることがあります。Apple は2つの方法を案内しています。File > Open を選び、Show Options をクリックして Ignore rich text commands を選択する方法、または TextEdit > Settings > Open and Save で Display HTML files as HTML code instead of formatted text を有効にする方法です。Apple: Mac の TextEdit で HTML 書類を操作する

サンプルと照らして完全なパスを確認する

以下が想定されるアウトラインの表示です。ラベルは中立的な仮のものです。番号はそれぞれの位置によって決まります。

1 Node A
  1.1 Node B
  1.2 Node C
    1.2.1 Node D
    1.2.2 Node E
  1.3 Node F
2 Node G
  2.1 Node H
3 Node I

このサンプルでは、3種類の異なる階層の移行を確認できます。Node D は Node C の下の第3階層に始まります。その枝が終わると、Node F で第2階層に戻ります。Node G で最上位に戻り、その最初の子の番号は 2.1 になります。サンプルツリーを決まった順序でたどり、想定されるパスを確認しました。準備中にブラウザーで表示したものではありません。

各 CSS ルールが重要な理由

リセットは .outline と .outline ol の両方に設定します。ネストされたリストごとに outline-section の新しいインスタンスが必要です。外側のリストだけをリセットしても、各階層に独立したカウントは作られません。各リスト項目でカウンターを増やすと、現在のインスタンスが進みます。MDN: CSS カウンターの使用

表示ルールでは counters(outline-section, ".") を使います。複数形の関数は、その名前を持つアクティブなインスタンスをすべて集めます。これを単数形の counter(outline-section) に置き換えると、現在の階層の値だけが表示され、入れ子の項目は完全なパスではなく 1 と表示されます。MDN: counters() CSS 関数

この例では独自の接頭番号を付けるため、標準のリストマーカーを取り除いています。また、各リストに role="list" を指定しています。MDN によると、そうしないと Safari では list-style-type: none を指定したリストがアクセシビリティツリーから省かれることがあります。入れ子のリストを追加する場合も、この属性を付けてください。MDN: list-style-type CSS プロパティ

構造を崩さずにアウトラインを編集する

入れ子の <ol> は、その枝を持つ <li> の中に置きます。たとえば Node C の子リストは、Node C の閉じタグ </li> より前に置きます。入れ子のリストを別の番号付きリストの直接の子にしてはいけません。MDN: HTML の番号付きリスト要素

実用的な編集テストとして、Node C の直前に新しい項目を追加してください。その新しいパスは 1.2、Node C は 1.3、Node D と Node E はそれぞれ 1.3.1 と 1.3.2 になるはずです。Node G は 2 のままです。これらの想定される変化はサンプルの階層構造から導かれるもので、番号が枝の構造に従っているかを確認するのに役立ちます。

枝を移動する場合は、ネストされたリストと閉じタグを含め、それを持つリスト項目全体を移動してください。ラベルだけを移動すると、子項目がその場に残ります。第4階層を作成するには、該当する項目の中に既存の入れ子リストの構造を繰り返します。ラベルに追加の番号を手入力しないでください。

子項目を連続した番号にする場合は、枝ごとに子リストを1つにします。この実装では、同じ親の下に兄弟となる子リストをもう1つ作ると、別のリセットが発生し、そのリスト内の番号は再び始まります。MDN の例では、同じ項目の下に別々のネストされたリストを置くと番号が再開する様子が示されています。MDN: CSS カウンターの使用

番号が正しくないときは順に確認する

まず構造を確認し、次にカウンタールール、最後に表示を確認します。インデントを変えても、欠けている入れ子の階層は修正できません。

表示されている状態 確認する箇所 この例での修正
すべてのネストされた項目に単一の番号が表示される content にある関数 区切り文字にピリオドを使い、複数形の counters() を使用します。
兄弟項目の番号がすべて 1 から始まる リセットのセレクター すべての項目ではなく、リストでリセットします。
完全なパスの横に標準の番号が表示される list-style ルール 外側と入れ子のリストに list-style-type: none を適用します。
枝に想定外の接頭番号が付く ネストされたリストの位置 子リストを、それを持つ項目の中に置きます。
別の場所にあるリストにも接頭番号が付く カウンターセレクターの範囲 ルールに .outline の接頭辞を残します。

項目が非表示になっていないかも確認してください。CSS カウンターの処理は、display: none が指定された要素など、ボックスを生成しない要素では実行されません。一方、visibility: hidden の要素はボックスを生成するため、カウントに影響することがあります。除外した項目が位置を占めるべきか、意図的に決めてください。W3C: ボックスを生成しない要素のカウンター

独自の開始番号を設定するには?

この例では、カウンターを0から開始し、1ずつ増やします。start や reversed などの HTML 属性や、リスト項目の value は、標準の番号付きリストの番号を制御します。ここに示した独自の outline-section ルールは、それらの属性を読み取りません。そのため、start="4" だけを追加しても、この例で表示される接頭番号は 4 から始まりません。HTML Standard: 番号付きリストとリスト項目

連番を飛ばしたり、枝の番号を降順にしたりする必要がある場合は、このスタイルシートを変更する前に要件を明確にしてください。独自の表示が無視する属性を追加するのではなく、表示されるパスを意図した番号付きリストの値と一致させてください。

番号を表示上のものとするか、文書の内容とするかを決める

このチュートリアルの接頭番号は CSS で生成されるコンテンツです。視覚的なアウトラインには適していますが、重要な参照として使う場合は別途検討が必要です。MDN によると、::before で追加したコンテンツはスクリーンリーダーで常に利用できるとは限りません。role="list" を追加すればリストとして認識されますが、生成されたパスがすべて読み上げられる保証にはなりません。MDN: ::before CSS 疑似要素

完全なパスを実際のテキストに含める必要がある場合(たとえばプレーンテキストで書き出す場合)は、文書内のテキストとして記載するか、書き出し時に生成してください。その場合はメンテナンスの前提が変わり、編集のたびに保存済みのパスも更新する必要があります。すべての出力形式で重要なテキストを視覚的な接頭番号に頼らないでください。

ローカルで使う視覚的なアウトラインでは、最後にサンプルの9つのパスを確認し、兄弟項目を挿入して、影響を受ける枝の番号が想定どおりに振り直されることを確かめてください。ブラウザーでこれらの確認ができたら、入れ子構造とソース順を保ちながら、例示用のラベルを実際のものに置き換えてください。

出典