Webサイトでリストを使う際、CSS標準の list-style-type
だけでは物足りないと感じたことはありませんか?
list-style-type: disc; /* ● */
list-style-type: circle; /* ○ */
list-style-type: square; /* ■ */
list-style-type: decimal; /* 1, 2, 3... */
list-style-type - CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/list-style-type
しかし、実務では以下のような多様なマーカーが求められます:
- 装飾的な数字:①②③、❶❷❸、⑴⑵⑶
- 装飾的なアルファベット:ⒶⒷⒸ、🅐🅑🅒、⒜⒝⒞
- カスタム画像:チェックマーク、企業ロゴ、アイコン
- カラフルなマーカー:赤い三角、青い四角
「リストマーカーを変更できます」: ここまでくらいは、ググれば記事が山ほど出てきます。
でもはっきり言って、ここまで「list-style-type」のカスタマイズ・解説してる人は他にいないです。
本記事では、CSS標準ではできないリストマーカーを実装する方法を、実用的なコード付きで徹底解説します。
1. CSS標準のlist-style-typeの問題点
1-1. 命名が直感的でない
CSS標準の命名は、実際の見た目と一致しません:
list-style-type: disc; /* ● 実際は "bullet" に近いサイズ */
list-style-type: circle; /* ○ "white-bullet" の方が適切 */
list-style-type: square; /* ■ "box" の方が直感的 */
1-2. 装飾的マーカーに対応していない
実務でよく求められるマーカーが使えません:
<!-- ❌ CSS標準では不可能 -->
<ol>
<li>① 囲み数字を使いたい</li>
<li>② でも標準では対応していない</li>
</ol>
1-3. カスタマイズの自由度が低い
色・サイズ・間隔を自由に調整できません。
2. なぜclassではなく属性セレクタを使うのか
2-1. WordPressエディターの挙動
WordPressのブロックエディター(Gutenberg)では、リストブロックで list-style-type
を指定すると、自動的にstyle属性として出力されます:
<!-- WordPressの出力 -->
<ul style="list-style-type:circle;">
<li>テキスト</li>
</ul>
classを使う方式だと、ユーザーがカスタムクラスを手動で追加する必要があり、運用コストが高くなります。
2-2. 属性セレクタの利点
/* ✅ style属性を直接ターゲット */
[style*="circled-decimal"]>li::before {
content: "①";
}
メリット:
- WordPressエディターでドロップダウンから選択するだけ
(※WPエディターもカスタマイズしている場合) - カスタムクラスの追加不要
- ユーザーフレンドリー
3. 実装方法の全体像
3-1. 基本戦略
/* ステップ1: 標準マーカーを非表示 */
[style*="circled-decimal"]>li {
list-style: none;
position: relative;
padding-left: 1.3em;
}
/* ステップ2: ::beforeで疑似要素を配置 */
[style*="circled-decimal"]>li::before {
position: absolute;
left: 0;
content: counter(list-counter);
}
/* ステップ3: Unicode文字で上書き */
[style*="circled-decimal"]:not([start])>li:nth-of-type(1)::before {
content: "①";
}
3-2. CSS Countersの活用
/* counterの初期化 */
[style*="circled-decimal"] {
counter-reset: ct-circled-decimal 0;
}
/* counterのインクリメント */
[style*="circled-decimal"]>li {
counter-increment: ct-circled-decimal;
}
4. Unicode文字を使った実装
4-1. 利用可能なUnicode文字
囲み数字(① ② ③ ...⑳)
- Unicode範囲: U+2460-2473
- 1-20まで対応
黒丸付き数字(❶ ❷ ❸ ...❿)
- Unicode範囲: U+2776-277F (1-10)
- Unicode範囲: U+24EB-24F4 (11-20)
囲み大文字アルファベット(Ⓐ Ⓑ Ⓒ ...)
- Unicode範囲: U+24B6-24CF (A-Z)
黒丸付き大文字(🅐 🅑 🅒 ...)
- Unicode範囲: U+1F150-1F169 (A-Z)
括弧付き大文字(⒜ ⒝ ⒞ ...)
- Unicode範囲: U+249C-24B5 (A-Z)
参考:
unicode-range - CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/@font-face/unicode-range
4-2. 実装例:circled-decimal
/* 囲み数字(① ② ③ ...) */
[style*="circled-decimal"] {
counter-reset: ct-circled-decimal 0;
}
[style*="circled-decimal"]>:is(li, dd) {
counter-increment: ct-circled-decimal;
position: relative;
padding-left: 1.3em;
list-style: none;
}
[style*="circled-decimal"]>:is(li, dd)::before {
position: absolute;
top: calc(0.5lh - 0.5em);
left: 0;
display: inline-flex;
place-content: center;
place-items: center;
min-width: 1em;
height: 1em;
/* デフォルト: counter表示 */
content: counter(ct-circled-decimal);
font-family: "Kumbh Sans", sans-serif;
}
/* Unicode文字で上書き(startなしの場合のみ) */
[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(1)::before { content: "①"; }
[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(2)::before { content: "②"; }
[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(3)::before { content: "③"; }
/* ... ⑳まで */
4-3. なぜこの順序なのか
重要なポイント:
/* ❌ 間違った順序 */
1. Unicode文字を先に定義
2. counter表示を後に定義
→ counterが優先されてUnicodeが表示されない
/* ✅ 正しい順序 */
1. counter表示を先に定義(デフォルト)
2. Unicode文字を後に定義(上書き)
→ :not([start]) の詳細度が高いため正しく上書き
参考:
counter() - CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/counter
5. start属性への対応
5-1. なぜstart属性が必要か
実務では、リストを途中から開始したいケースがあります:
<ol style="list-style-type:circled-decimal;">
<li>① テキスト</li>
<li>② テキスト</li>
</ol>
<p>途中に別のコンテンツ</p>
<ol start="3" style="list-style-type:circled-decimal;">
<li>③ テキスト</li>
<li>④ テキスト</li>
</ol>
5-2. start属性の実装
/* すべてのリストでcounter-reset */
[style*="circled-decimal"] {
counter-reset: ct-circled-decimal 0;
}
/* start属性に応じて調整(1-20対応) */
[style*="circled-decimal"][start="1"] { counter-reset: ct-circled-decimal 0; }
[style*="circled-decimal"][start="2"] { counter-reset: ct-circled-decimal 1; }
[style*="circled-decimal"][start="3"] { counter-reset: ct-circled-decimal 2; }
[style*="circled-decimal"][start="4"] { counter-reset: ct-circled-decimal 3; }
/* ... start="20"まで */
参考:
HTML 属性リファレンス - HTML | MDN
https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Attributes
5-3. Unicode vs Counter
/* startなし: Unicode文字(綺麗) */
[style*="circled-decimal"]:not([start])>li:nth-of-type(1)::before {
content: "①";
}
/* start属性あり: counter表示(統一感) */
[style*="circled-decimal"][start]>li::before {
content: counter(ct-circled-decimal);
/* 必要に応じてbackground等でスタイリング */
}
6. カスタム画像マーカーの実装
6-1. CSS Custom Propertiesを活用
/* カスタムプロパティで画像URLを指定 */
[style*="list-style-image"]>:is(li, dd) {
position: relative;
padding-left: 2em;
list-style: none;
}
[style*="list-style-image"]>:is(li, dd)::before {
position: absolute;
top: calc(0.5lh - 0.5em);
left: 0;
content: "";
min-width: 1.5em;
height: 1.5em;
background: no-repeat 50% 50% / contain;
background-image: var(--list-style-image);
}
6-2. HTML での使用例
<!-- カスタム画像マーカー -->
<ul style="--list-style-image:url('https://example.com/icon/check.svg');">
<li>チェックマーク付きアイテム</li>
<li>アイコンは自由に変更可能</li>
</ul>
<!-- 別の画像に変更 -->
<ul style="--list-style-image:url('https://example.com/icon/star.svg');">
<li>星アイコン付きアイテム</li>
</ul>
6-3. メリット
運用の柔軟性:
- 画像URLをHTMLで簡単に変更可能
- CSSを編集する必要なし
- プロジェクトごとに異なるアイコンを使用可能
7. フォント選定の重要性
7-1. Noto Sans JPの問題
Noto Sans JPは優れた日本語フォントですが、上部余白が広い設計のため、height: 1em
の円や四角の中で中央揃えにすると下にズレます。
/* ❌ Noto Sans JPだと下にズレる */
font-family: "Noto Sans JP", sans-serif;
参考:
じゃあなんすか、Noto Sans JP使うんならボタンにアイコン入れるなって事すか|chot Inc. デザイナーユニット
https://note.com/chot_designer/n/n85ef47a2ec69
(※実際は、<button><span style="postion:relative; top:0.625em;">テキスト</span></button>
等で解消できますが、少しめんどくさい)
7-2. 推奨フォント:Kumbh Sans
検証の結果、Kumbh Sansが最適でした:
メリット:
- ✅ 上下余白が均等(ミドル揃えに最適)
- ✅
height: 1em
の円・四角で真ん中に配置 - ✅ ウェイト200-900まで対応
- ✅ クセがなく視認性が良い
- ✅ Google Fontsで無料利用可能
<!-- Google Fontsで読み込み -->
<link href="https://fonts.googleapis.com/css2?family=Kumbh+Sans:wght@200..900&display=swap" rel="stylesheet">
/* Unicode文字・counterに適用 */
[style*="circled-decimal"]>li::before {
font-family: "Kumbh Sans", sans-serif;
font-weight: 500;
7-3. 他の候補
以下のフォントも良好な感じでした:
- Hanken Grotesk
- Lato
- Roboto Flex
- Outfit(少しクセが強い/デザイン性がある)
8. 完全なSCSSコード
8-1. 基本スタイル
/* ========================================
Circled Decimal (①②③...)
======================================== */
/* すべてのリストでcounter初期化 */
:is(ul, ol, dl)[style*="circled-decimal"] {
counter-reset: ct-circled-decimal 0;
}
/* start属性に応じた調整(1-20対応) */
:is(ul, ol, dl)[style*="circled-decimal"][start="1"] { counter-reset: ct-circled-decimal 0; }
:is(ul, ol, dl)[style*="circled-decimal"][start="2"] { counter-reset: ct-circled-decimal 1; }
:is(ul, ol, dl)[style*="circled-decimal"][start="3"] { counter-reset: ct-circled-decimal 2; }
/* ... start="20"まで */
/* counterインクリメント */
:is(ul, ol, dl)[style*="circled-decimal"]>:is(li, dd) {
counter-increment: ct-circled-decimal;
position: relative;
padding-left: 1.3em;
list-style: none;
}
/* 疑似要素の配置 */
:is(ul, ol, dl)[style*="circled-decimal"]>:is(li, dd)::before {
position: absolute;
top: calc(0.5lh - 0.5em);
left: 0;
display: inline-flex;
place-content: center;
place-items: center;
min-width: 1em;
height: 1em;
/* デフォルト: counter表示 */
content: counter(ct-circled-decimal);
font-family: "Kumbh Sans", sans-serif;
font-weight: 500;
}
/* startなしの場合: Unicode文字で上書き */
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(1)::before { content: "①"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(2)::before { content: "②"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(3)::before { content: "③"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(4)::before { content: "④"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(5)::before { content: "⑤"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(6)::before { content: "⑥"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(7)::before { content: "⑦"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(8)::before { content: "⑧"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(9)::before { content: "⑨"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(10)::before { content: "⑩"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(11)::before { content: "⑪"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(12)::before { content: "⑫"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(13)::before { content: "⑬"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(14)::before { content: "⑭"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(15)::before { content: "⑮"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(16)::before { content: "⑯"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(17)::before { content: "⑰"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(18)::before { content: "⑱"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(19)::before { content: "⑲"; }
:is(ul, ol, dl)[style*="circled-decimal"]:not([start])>:is(li, dd):nth-of-type(20)::before { content: "⑳"; }
8-2. その他のスタイル
同様の方式で以下も実装可能:
filled-circled-decimal
(❶ ❷ ❸ ...)circled-upper-alpha
(Ⓐ Ⓑ Ⓒ ...)filled-circled-upper-alpha
(🅐 🅑 🅒. ..)parenthesized-upper-alpha
(⒜ ⒝ ⒞ ...)
8-3. カスタム画像
/* ========================================
Custom Image Markers
======================================== */
:is(ul, ol, dl)[style*="list-style-image"]>:is(li, dd) {
position: relative;
padding-left: 2em;
margin-block: 0.5rem;
list-style: none;
}
:is(ul, ol, dl)[style*="list-style-image"]>:is(li, dd)::before {
position: absolute;
top: calc(0.5lh - 0.5em);
left: 0;
content: "";
min-width: 1.5em;
height: 1.5em;
background: no-repeat 50% 50% / contain;
background-image: var(--list-style-image);
}
9. 実用例とデモ
9-1. 基本的な使用例
<!-- 囲み数字 -->
<ol style="list-style-type:circled-decimal;">
<li>最初のアイテム</li>
<li>2番目のアイテム</li>
<li>3番目のアイテム</li>
</ol>
<!-- 途中から開始 -->
<ol start="4" style="list-style-type:circled-decimal;">
<li>4番目から開始</li>
<li>5番目のアイテム</li>
</ol>
9-2. 複数スタイルの組み合わせ
<!-- 黒丸付き数字 -->
<ol style="list-style-type:filled-circled-decimal;">
<li>重要なポイント</li>
<li>強調したい内容</li>
</ol>
<!-- 囲みアルファベット -->
<ol style="list-style-type:circled-upper-alpha;">
<li>選択肢A</li>
<li>選択肢B</li>
<li>選択肢C</li>
</ol>
<!-- カスタム画像 -->
<ul style="--list-style-image:url('/img/icon/check.svg');">
<li>完了したタスク</li>
<li>チェック済みアイテム</li>
</ul>
9-3. dlタグでの使用
<!-- 定義リストでも動作 -->
<dl style="list-style-type:circled-decimal;">
<dt>見出し要素</dt>
<dd>① 説明文</dd>
<dd>② さらに詳しい説明</dd>
</dl>