なぜ今さら「CSSカウンター」なのか?―olタグの限界を超えた連番制御の極意
現場でコーディングをしていると、時折「このデザイン、標準の`
- `タグじゃ構造的に無理があるな…」という壁にぶち当たることがありますよね。例えば、ネストしたカスタムコンポーネントの中で連番を振りたい時や、デザイン要件で複雑な階層構造(1.1, 1.1.2…のような)を求められた時です。
- 概要
- 背景
- 目的
- 実装
- セットアップ
- コンポーネント化
- DOMのクリーンさ: HTMLの中に「1.」「1-1」といった番号をハードコーディングする必要がありません。もしデザイン変更で番号が不要になったり、形式が変わったりした際も、CSSの1行を修正するだけでサイト全体に反映されます。
- ブラウザ最適化: JavaScriptによるDOM操作は、レンダリングコストが高い処理です。CSSカウンターはブラウザの内部的な描画レイヤーで行われるため、特にリストの数が数千件規模になってもパフォーマンスへの影響は微々たるものです。
「とりあえずJavaScriptで番号を振っておこう」と安易に考えていませんか? それ、実はCSSカウンターを使えば、DOMを汚さず、かつブラウザのレンダリングパイプラインを最大限に活かしたスマートな解決が可能です。
今日は、中級エンジニアなら押さえておきたい、CSSカウンターを使った「脱・機械的なマークアップ」の技術論を解説します。
—
CSSカウンターの仕組み:ブラウザはどう処理しているか
CSSカウンターは、実はCSS変数の祖先のような存在です。ブラウザはレンダリングエンジンの中で、文書ツリーとは別に「カウンターテーブル」という内部状態を保持しています。
1. `counter-reset`: カウンターの値を初期化します。いわば変数の宣言です。
2. `counter-increment`: 要素がレンダリングされるタイミングで、カウンターの値を加算します。
3. `content: counter(…)`: 擬似要素(`::before` や `::after`)を通じて、現在の値を実際のDOMに描画します。
重要なのは、「DOM上の位置関係がそのままカウンターのライフサイクルに直結する」という点です。JavaScriptで要素を動的に追加・削除しても、CSS側は自動的に再計算(Re-layout/Repaint)してくれるため、番号のズレを気にする必要がありません。
—
実践:階層構造を持つカスタムリストの構築
例えば、ドキュメントの章立てや、複雑なナビゲーションでよくある「親番号.子番号」のような形式をCSSだけで作ってみましょう。ここでは、汎用性の高い`ul`要素に独自の番号を付与するパターンを紹介します。
/ カウンターの設計 /
.custom-list {
/ リスト全体でカウンターを初期化 /
counter-reset: section;
list-style: none;
padding-left: 0;
}
.custom-list > li {
/ 項目ごとにカウンターをインクリメント /
counter-increment: section;
}
.custom-list > li::before {
/ counter()で現在の値を文字列として生成 /
content: “Chapter ” counter(section) “. “;
font-weight: bold;
color: #007bff;
margin-right: 8px;
}
/ さらにネストしたサブリストの制御 /
.sub-list {
counter-reset: subsection; / 子リストでカウンターをリセット /
padding-left: 20px;
}
.sub-list > li {
counter-increment: subsection;
}
.sub-list > li::before {
/ 親のカウンター(section)と子のカウンター(subsection)を組み合わせる /
content: counter(section) “.” counter(subsection) ” “;
color: #666;
}
なぜこれが「プロの選択」なのか
このアプローチの最大のメリットは、「アクセシビリティ」と「パフォーマンス」の両立です。
現場で陥りやすい罠:注意点
最後に、現場でこの技術を使う際の「落とし穴」を2つだけ共有しておきます。
1. ブラウザの検索機能: CSSの `content` で生成されたテキストは、Ctrl+F(検索)ではヒットしません。もし「ユーザーが番号をテキストとして選択・検索する」必要がある場合は、CSSカウンターではなく、マークアップ側で番号を振るのが誠実な設計です。
2. `display: none` の扱い: CSSカウンターは、レンダリングツリーに乗らない要素(`display: none`)ではインクリメントされません。逆に `visibility: hidden` であればカウントは進みます。この仕様の違いは、デバッグ時に必ず一度はハマるポイントなので覚えておいてください。
—
CSSカウンターは、派手なアニメーションを作るわけではありませんが、「フロントエンドのコードをいかに美しく、かつ保守しやすく保つか」というプロのこだわりを体現する技術です。
「動けばいい」というコードから、「設計意図が明確なコード」へ。ぜひ明日からの実装で、このカウンターの力を試してみてください。何か不明な点があれば、またいつでも相談してくださいね。応援しています。

コメント