CSSの表現力を極める:`@counter-style` でリストマーカーの「既成概念」を破壊する
フロントエンド開発の現場で、デザイナーから「このリストのマーカー、普通の丸や数字じゃなくて、ブランドの象徴的なアイコンにしたいんだよね」と無茶振り(?)を受けたことはありませんか?
これまでの僕らなら、`list-style-image` で画像を指定するか、あるいは `::before` 擬似要素で `content` をゴリゴリ書き換えるハックに頼っていたはずです。しかし、`::before` でリストマーカーを制御するのは、アクセシビリティの観点や、リストが折り返した時のインデント制御において、往々にして「保守性の地獄」を招きます。
そこで今こそ活用すべきなのが、CSSの `@counter-style` です。これを使えば、ブラウザのレンダリングエンジンに直接「新しいカウント方式」を教え込むことができます。今回は、この強力な武器を実務レベルで使いこなすための知見を共有しましょう。
—
なぜ `::before` ではなく `@counter-style` なのか
`::before` による実装は、いわば「見た目を上書きしているだけ」の状態です。リストのセマンティクス(意味構造)とは切り離されており、スクリーンリーダーがマーカーをどう解釈するかは不安定になりがちです。
一方で `@counter-style` は、「リストのナンバリングシステムそのものをCSSで定義する」というアプローチをとります。これにより、以下のメリットが生まれます。
1. セマンティクスの維持: `
- ` や `
- 装飾目的の場合: 装飾的な絵文字であれば `aria-hidden=”true”` を検討するか、そもそも `list-style` を使わずに `role=”presentation”` でリスト構造を殺すことも視野に入れてください。
- 意味がある場合: 数字を独自の記号に置き換えるなら、ユーザーが情報の順序を理解できるか確認してください。
- ` の構造を汚さず、CSSのみでデザインを完結できる。
2. ブラウザネイティブな挙動: リストマーカーとしての配置や、テキストが折り返された際のインデント(`list-style-position`)がブラウザの標準的な処理に従う。
3. 再利用性: 一度定義すれば、`list-style` プロパティで切り替えるだけで、サイト全体で統一感のあるマーカーを運用できる。
—
実践:`@counter-style` を使ったカスタムリストの構築
例えば、「進捗ステップ」を表すリストで、ただの数字ではなく「絵文字」を使いたいとします。単純に `::before` で出すとテキストと重なったりして調整が面倒ですが、`@counter-style` なら以下のように書けます。
/ 独自のカウンターを定義 /
@counter-style emoji-steps {
system: cyclic; / 数値が終わったら繰り返す /
symbols: “🚀” “🛠️” “✅”; / 使うマーカーの指定 /
suffix: ” “; / マーカーの後の余白 /
}
/ 適用するリストのクラス /
.step-list {
list-style: emoji-steps inside; / 定義した名前を適用 /
padding-left: 0;
}
ブラウザの裏側で何が起きているのか?
ブラウザは `list-style` に指定された識別子を、定義された `@counter-style` ルールと照合します。`system` プロパティが `fixed`(指定数で止まる)なら最後の数値で固定され、`cyclic`(今回のように)ならループします。ブラウザは内部的に「このリストのマーカーはどの状態か?」を管理しており、DOMの更新に合わせてマーカー部分だけを効率的に再描画しています。これは、手動で擬似要素を操作するよりも、ブラウザのレイアウトエンジンにとって非常に親切なやり方なのです。
—
実務で役立つ「クオリティアップ」のTips
単に絵文字を出すだけなら簡単ですが、実務ではもう少し凝った要件が求められます。特に「特定の言語のナンバリング」や「前後に装飾を付けたい」というケースです。
応用例:固定の接頭辞を持つインデックス
@counter-style custom-numeric {
system: numeric; / 数値を自動生成 /
symbols: “0” “1” “2” “3” “4” “5” “6” “7” “8” “9”;
prefix: “Step-“; / 前に固定文字を入れる /
suffix: “: “; / 後ろにセパレーターを入れる /
}
.process-list {
list-style: custom-numeric;
}
このコードを適用すると、`
- ` の中身は `Step-1: `, `Step-2: ` と自動的にナンバリングされます。HTML側でわざわざ手打ちする必要はありません。これぞエンジニアリングです。
—
注意点:アクセシビリティへの配慮
最後に、シニアとして忠告しておきたいことがあります。`@counter-style` は強力ですが、スクリーンリーダーが「何を読み上げるか」には注意が必要です。
結論:CSSの可能性を諦めない
`@counter-style` は、CSSという言語が単なる「色や配置を決める道具」を超えて、「構造を表現するツール」へと進化している証拠です。
「CSSでどこまでできるか」を知っておくことは、JavaScriptに頼り切らずに、より軽量で堅牢なUIを作るための必須教養です。明日からの実装で、ぜひこの魔法のようなルールを試してみてください。もしコードがうまく動かなければ、まずはブラウザのデベロッパーツールで `list-style` が正しく計算されているか確認することから始めてみてくださいね。
皆さんの実装が、少しでも美しく、そして「賢い」ものになることを期待しています。

コメント