普通のHTMLの書き方では目次にol要素を使っ(てしまっ)たが、対応する見出しには番号がない。これはあまり良くなさそうだが、文字列として追加するのも何だな……とCSSでやろうとしていた。ひとつのカウンターを作って、counters()による連結だけでいけるかと考えたが、うまくいかなかった。

セクションのレベルごとにcouter()を作る、というのが結論だ。counters()はこのケースでは利用できない、というよりもほぼリスト系のためだけに存在することがわかった。

counters()の利用

対象のHTML文書では、sectionh1要素のみというストイックな記述になっている。そのためsection要素でリセットして、h1要素でインクリメントすれば良いだろうと、まずは以下のように書いた。

section {
  counter-reset: section;
}

section h1::before {
  content: counters(section, ".") ". ";
  counter-increment: section;
}

これだとすべての見出しに「1.」や「1.1.」が付く。当たり前だがsection要素ごとにリセットされるからだ。このパターンはolli要素のような構造ではうまく振られる。

counter-resetの工夫

数字をリセットするタイミングが悪い、と勘違いした。そのため次はそれを制御しようと以下のように書いた。

main,
main > section {
  counter-reset: section;
}

section h1::before {
  content: counters(section, ".") ". ";
  counter-increment: section;
}

これだとカウンターとその利用がうまくマッピングされない。そのため「0.1」や「0.3」などセクションのネストを無視した連番になってしまう。

複数のカウンターの利用

落ち着いて考えるとcounters()で連番を振れるのはネストしあう要素同士だけで、それに含まれる要素へは無理だった。最後にたどり着いたのはカウンターを大見出しと小見出しでわける方法だ。

main {
  counter-reset: section;
}

main > section {
  counter-reset: subsection;
}

main > section > h1::before {
  content: counter(section) ". ";
  counter-increment: section;
}

main > section > section > h1::before {
  content: counter(section) "." counter(subsection) ". ";
  counter-increment: subsection;
}

section要素がある分、h1h2要素でフラットな構造を持つ場合と違って複雑になるが、これでうまく振れる。


結局は採用を見送ったので、もはやどうでも良い話だ。頭の体操と仕様を見直すきっかけにはなった。

CSSのカウンターは、CSS2時代から存在するが、Internet Explorer 6などで実装されなかったので日の目を見なかった。ChromeやFirefoxでのサポートで一瞬脚光を浴びたが、同時に使いどころがあまりないことも判明してしまい、そのまま忘れ去られたのだろう。