CSSのcounters()関数を使った番号付きリストのスタイル設定



Webページで番号付きリストを作成する際は、olタグを使って項目の順序付きリストとして番号を表示していきますが、シンプルな数字のほか、type属性の設定でのローマ数字やアルファベットくらいしか表現方法がありません。

CSSのcounters()関数を使うことで、カウンターによる連番を生成することができ、olタグのリストスタイルではなくオリジナルのデザインで番号をつけることができます。
主にbefore、after擬似要素で利用していくので、かなり自由に番号を装飾することができます。

気になるWebブラウザのサポート状況ですが、すべてのブラウザでサポートされていますので問題なく利用できます。

Can I use (CSS Counters)
https://caniuse.com/?search=CSS%20Counters

counters関数に関連するプロパティは以下の4つとなります。

content
生成されたコンテンツを挿入する
counter-reset
カウンターの作成またはリセット
既定値は「0」となります。
プログラミングでいうところのインスタンス(実体)の生成や初期化をする作業
counter-increment
カウンターの値を更新する。(増加または減少)
counter()
名前付きカウンターの現在の値を返す


通常のolタグでは表現できない記号を使ってデザインしていく場合には、とても便利に活用できる関数です。

ここからは、サンプルを交えてcounters関数の使い方をご紹介していきます。

counters関数を使って連番を生成


番号付きリストのコンテンツをサンプルとして見ていきます。
以下、HTMLとCSSのサンプルコードになります。

HTML

<div class="content-wrap">
  <h2>Ordered Lists</h2>
  <ol>
    <li>First item</li>
    <li>Second item</li>
    <li>Third item</li>
    <li>Fourth item</li>
  </ol>
</div>

CSS

body {
  background-color: #ddd;
  counter-reset: cnt;
}

.content-wrap {
  width: calc(100% - 1.875rem);
  max-width: 600px;
  margin: 1.875rem auto;
  padding: 1.875rem;
  background-color: #ddd;
}

.content-wrap h2 {
  padding: 0 0 1rem;
}

/*///// Ordered Lists /////*/
ol li {
  list-style: none;
}

ol li::before {
  counter-increment: cnt;
  content: counter(cnt) ") ";
}


body要素にcounter-resetプロパティを設定しています。
counter-resetでのカウンターの作成・リセットは、親要素より上の階層はカウントすることができないので、カウントを取る対象の要素の親要素やそれより上の階層で設定します。
カウンター名はcountを省略して「cnt」としています。

body要素のほか、CSSクラス「content-wrap」や「content-wrap h2」のスタイルでは、簡単に見た目を整えています。この後の説明では共通のスタイルとします。

リストのbefore擬似要素にて、counter-incrementプロパティの引数にカウンター名を指定して、リストのカウントを生成します。そして、contentプロパティで生成されたコンテンツを挿入します。文字列などはダブルクォーテーションで区切って挿入します。

以下、実行結果になります。

counters関数を使って連番を生成



contentプロパティでは、半角スペースを空けて複数の値を設定できるので、カウントの前と後ろにカッコを挿入するなどしてもいいでしょう。

ol li::before {
  content: "(" counter(cnt) ") ";
}
番号付きリストにカッコをつける



このように、通常のolタグでは表現できない記号を使っていくことができます。
ちなみに、HTMLの特殊文字を使う場合は16進数を扱い、さらに変換する必要があります。



また、counter関数の第2引数に「decimal-leading-zero」を指定すると、連番の様式は「01 02 03 ..」のように2桁で表すこともできます。
以下、番号付きリストの装飾も含めて、リスト要素のCSSのサンプルになります。

ol li {
  list-style: none;
  padding: 1rem 0;
}

ol li::before {
  counter-increment: cnt;
  content: counter(cnt, decimal-leading-zero);
  color: #fff;
  font-weight: bold;
  background-color: #00f;
  padding: 0.8rem;
  border-radius: 50%;
  margin: 0 0.5rem 0 0;
}
CSSのcounter関数を使った番号付きリストの装飾



複数行になる場合の2行目以降の字下げは、装飾した擬似要素に合わせて「padding-left」と「text-indent」の数値を指定してください。
サンプルではリストの上下に内側の余白を指定していますので、ショートハンドでpadding-leftを指定していきます。

ol li {
  list-style: none;
  padding: 1rem 0 1rem 3.4rem;
  text-indent: -3.4rem;
}
リスト要素の2行目以降のインデント

入れ子構造の場合


HTML構造が入れ子になっている場合でも、階層間で連番を生成することができます。
入れ子の場合はcounter()ではなく、counters()を使って階層間で区切り文字を挿入していきます。

以下、HTMLとCSSのサンプルです。

HTML

<div class="content-wrap">
  <h2>Ordered Lists</h2>
  <ol>
    <li>item</li>
    <li>item</li>
    <li>item
      <ol>
        <li>item</li>
        <li>item</li>
        <li>item</li>
      </ol>
    </li>
    <li>item</li>
  </ol>
</div>

CSS

body {
  background-color: #ddd;
}

.content-wrap {
  width: calc(100% - 1.875rem);
  max-width: 600px;
  margin: 1.875rem auto;
  padding: 1.875rem;
  background-color: #fff;
}

.content-wrap h2 {
  padding: 0 0 1rem;
}

/*///// Ordered Lists (Nesting) /////*/
ol {
  counter-reset: cnt;
  list-style: none;
}

ol li::before {
  counter-increment: cnt;
  content: counters(cnt, ".") ". ";
}

ol ol {
  padding: 0 0 0 1rem;
}



HTMLはよくある入れ子構造を構築しました。
CSSのポイントとしては、親要素にcounter-resetプロパティの定義、そしてカウンターの関数は複数形の「counters()」を使います。
counter-resetを親要素に定義することで、入れ子になったolタグも0からカウントされます。そして、counters()関数で親要素のカウンタは保持したまま、入れ子になったリストは0からインクリメントで1ずつ足してカウントします。

CSSのcounters関数を入れ子構造で使う

リスト要素以外でも


CSSのcounters関数はリスト要素だけでなく、各セクションのコンテンツにナンバリングする時にも便利に使えるでしょう。
例えば、「章」「節」「項」などの章立てのコンテンツなど。

以下、セクションごとで番号を振る場合のサンプルです。

HTML

<section class="chapter-box">
  <h2>Title</h2>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
</section>

<section class="chapter-box">
  <h2>Title</h2>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
</section>

<section class="chapter-box">
  <h2>Title</h2>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
</section>

CSS

body {
  background-color: #ddd;
  counter-reset: chapter-box;
}

.content-wrap {
  width: calc(100% - 1.875rem);
  max-width: 600px;
  margin: 1.875rem auto;
  padding: 1.875rem;
  background-color: #fff;
}

.content-wrap h2 {
  padding: 0 0 1rem;
}

/*///// Chapter /////*/
.chapter-box {
  width: calc(100% - 1.875rem);
  max-width: 1000px;
  margin: 1.875rem auto;
  padding: 1.875rem;
  background-color: #fff;
}

.chapter-box h2::before {
  counter-increment: chapter-box;
  content: "Chapter " counter(chapter-box) " : ";
}



HTMLでは、各セクションにCSSクラス「chapter-box」を付与してCSSで管理してようにしておきます。
CSSでは、body要素にcounter-resetプロパティを設定、カウンター名は「chapter-box」としました。
あとは先程と同じように、カウントする対象の要素「.chapter-box h2」の見出しに対して、カウントして連番を付けます。
以下、実行結果になります。

CSSのcounters関数を使ってコンテンツの各セクションに番号をつける



また、各Chapterの中で親要素のカウントを継承しながら、Chapter内でもカウントをしていくといった場合もあるでしょう。
この場合、HTML構造は入れ子ではないので少し複雑になりますが、counter-resetプロパティを設定する要素が重要になります。
以下、サンプルコードになります。

HTML

<section class="chapter-box">
  <h2>Title</h2>
  <h3>Sub Title</h3>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
  <h3>Sub Title</h3>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
</section>

<section class="chapter-box">
  <h2>Title</h2>
  <h3>Sub Title</h3>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
  <h3>Sub Title</h3>
  <p>Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. Sample text. </p>
</section>

CSS

body {
  background-color: #ddd;
  counter-reset: chapter-box;
}

.content-wrap {
  width: calc(100% - 1.875rem);
  max-width: 600px;
  margin: 1.875rem auto;
  padding: 1.875rem;
  background-color: #fff;
}

.content-wrap h2 {
  padding: 0 0 1rem;
}

/*///// Chapter /////*/
.chapter-box {
  width: calc(100% - 1.875rem);
  max-width: 1000px;
  margin: 1.875rem auto;
  padding: 1.875rem;
  background-color: #fff;
  counter-reset: chapter-subbox;
}

.chapter-box h2::before {
  counter-increment: chapter-box;
  content: "Chapter " counter(chapter-box) " : ";
}

.chapter-box h3::before {
  counter-increment: chapter-subbox;
  content: counter(chapter-box) "." counter(chapter-subbox) " ";
}



counter-resetプロパティをさらにその外の要素(サンプルではbody要素)に設定しているので問題ありませんが、Chapterのブロック内のh3は、別のChapterブロック内のh3もカウントされないように、「counter-reset: chapter-subbox;」の設定は、CSSクラス「chapter-box」ごとでカウントするようにします。

CSSのcounter関数を別のセクションのカウントを継承しながら使う



counter-resetプロパティを設定した要素より下の階層で対象の要素をカウントする」という基礎的な部分を理解することで、コンテンツに対して自由に連番を振っていくことができます。

掲載するコンテンツのナンバリングや何かの手順・ステップなど、いろんな用途で使っていけそうですね。

さまざまなカウンターの設定方法


カウンターの作成またはリセットは、一行で複数のカウンターを設定することもできます。
カウンター名の後に半角スペースを空けて既定値の数値を設定します。

body {
  counter-reset: chapter-box cnt;
}



他にも、カウンターの初期値を指定することもできます。
初期値を指定する場合は、カウンター名の後に半角スペースを空けて、初期値とした数値を設定します。

body {
  counter-reset: chapter-box 5 cnt;
}


上記の場合、chapter-boxでカウントされた表示は6から始まります。
cntは既定値は「0」のままです。

こだわりのある番号付きリストのデザインの作成や、要素の数をカウントして自動で連番をつけてデザインしていきたい時には、CSSのcounters()関数を使ってデザインしてみてください。