第16章 列表与计数器属性

第十六章:列表与计数器属性

列表是我们日常生活中经常遇到的东西——购物清单、待办事项、排行榜… CSS 提供了丰富的列表样式控制。而 CSS 计数器更是一个"隐藏的神器",它可以让你不依赖 JavaScript 就能实现自动编号功能。这一章,我们将一起探索列表和计数器的奥妙。

16.1 列表属性

16.1.1 list-style-type——disc(实心圆)、circle(空心圆)、square(方块)、decimal(数字)、none(无标记)

list-style-type 是控制列表标记样式的属性。默认的列表标记可能是圆点、数字或者字母,但通过这个属性,你可以把它们变成任何你想要的样式。

什么是列表标记?

想象一下超市的购物清单,每一项前面都有一个"√“或者序号。"√“或序号就是列表标记。list-style-type 就是控制这些标记样式的属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* list-style-type 的各种值 */

/* 无序列表标记类型 */
.ul-disc {
  list-style-type: disc;    /* 实心圆(默认)*/
}

.ul-circle {
  list-style-type: circle;  /* 空心圆 */
}

.ul-square {
  list-style-type: square;  /* 方块 */
}

.ul-none {
  list-style-type: none;    /* 无标记 */
}

/* 有序列表标记类型 */
.ol-decimal {
  list-style-type: decimal;           /* 阿拉伯数字(1, 2, 3...)*/
}

.ol-decimal-leading-zero {
  list-style-type: decimal-leading-zero; /* 带前导零的数字(01, 02, 03...)*/
}

.ol-lower-alpha {
  list-style-type: lower-alpha;     /* 小写字母(a, b, c...)*/
}

.ol-upper-alpha {
  list-style-type: upper-alpha;     /* 大写字母(A, B, C...)*/
}

.ol-lower-roman {
  list-style-type: lower-roman;     /* 小写罗马数字(i, ii, iii...)*/
}

.ol-upper-roman {
  list-style-type: upper-roman;     /* 大写罗马数字(I, II, III...)*/
}

.ol-lower-greek {
  list-style-type: lower-greek;     /* 小写希腊字母(α, β, γ...)*/
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<h3>无序列表标记类型</h3>
<ul class="ul-disc">
  <li>实心圆(默认)</li>
  <li>列表项二</li>
  <li>列表项三</li>
</ul>

<ul class="ul-circle">
  <li>空心圆</li>
  <li>列表项二</li>
</ul>

<ul class="ul-square">
  <li>方块</li>
  <li>列表项二</li>
</ul>

<ul class="ul-none">
  <li>无标记(常用于自定义图标列表)</li>
  <li>列表项二</li>
</ul>

<h3>有序列表标记类型</h3>
<ol class="ol-decimal">
  <li>阿拉伯数字(1, 2, 3...)</li>
  <li>列表项二</li>
</ol>

<ol class="ol-decimal-leading-zero">
  <li>带前导零(01, 02, 03...)</li>
  <li>列表项二</li>
</ol>

<ol class="ol-lower-alpha">
  <li>小写字母(a, b, c...)</li>
  <li>列表项二</li>
</ol>

<ol class="ol-upper-roman">
  <li>大写罗马数字(I, II, III...)</li>
  <li>列表项二</li>
</ol>

list-style-type 的实用场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* 1. 自定义图标列表(先清除默认标记)*/
.icon-list {
  list-style-type: none;  /* 先清除默认标记 */
  padding-left: 0;
  margin: 0;
}

.icon-list li {
  padding-left: 24px;     /* 给图标留出空间 */
  position: relative;       /* 用于定位图标 */
  margin-bottom: 8px;
}

.icon-list li::before {
  content: "✓";            /* 自定义图标字符 */
  position: absolute;
  left: 0;
  color: #2ecc71;         /* 绿色对勾 */
  font-weight: bold;
}

/* 2. 导航菜单列表 */
.nav-list {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  gap: 20px;
}

.nav-list li a {
  text-decoration: none;
  color: #333;
}

/* 3. 步骤指示器 */
.step-list {
  list-style-type: none;
  counter-reset: step-counter;
  padding: 0;
}

.step-list li {
  counter-increment: step-counter;
  padding-left: 40px;
  position: relative;
  margin-bottom: 20px;
}

.step-list li::before {
  content: counter(step-counter);
  position: absolute;
  left: 0;
  width: 28px;
  height: 28px;
  background-color: #3498db;
  color: white;
  border-radius: 50%;
  text-align: center;
  line-height: 28px;
  font-weight: bold;
}

list-style-type 缩写:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* list-style-type 可以和其他属性一起缩写 */
/* list-style: type position image; */

/* 完整写法 */
.full-style {
  list-style-type: square;
  list-style-position: outside;
  list-style-image: none;
}

/* 缩写写法 */
.shorthand-style {
  list-style: square outside none;
}

/* 最简写法 */
.minimal-style {
  list-style: none;  /* 清除所有列表样式 */
}

💡 小技巧:当你想使用自定义图标作为列表标记时,首先需要设置 list-style-type: none 来清除默认标记,然后通过 ::before 伪元素来添加自定义图标。这是最灵活的自定义列表标记方式。

16.1.2 list-style-position——inside(标记在内容区内)、outside(标记在内容区外,默认)

list-style-position 控制列表标记相对于列表项内容的位置——是"在内容里面"还是"在内容外面”。

什么是列表标记位置?

想象一下书的目录,每一章的标题前有序号。如果序号紧贴着标题文字,那就是"inside”(里面);如果序号在标题左边一点的位置,那就是"outside"(外面)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* list-style-position 的两种值 */

/* outside —— 标记在内容区外(默认)*/
.position-outside {
  list-style-position: outside;  /* 默认值 */
  background-color: #f8f9fa;
  padding: 16px;
  margin-bottom: 20px;
}

/* inside —— 标记在内容区内 */
.position-inside {
  list-style-position: inside;
  background-color: #e8f4f8;
  padding: 16px;
}

.list-item {
  background-color: white;
  padding: 12px;
  margin-bottom: 8px;
  border-left: 3px solid #3498db;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<ul class="position-outside">
  <li class="list-item">
    <strong>outside(默认)</strong> - 标记在内容区外面。
    标记和第一行文字对齐,第二行文字会缩进。
    这是一个比较长的列表项,用于演示outside的效果。
  </li>
  <li class="list-item">第二项</li>
</ul>

<ul class="position-inside">
  <li class="list-item">
    <strong>inside</strong> - 标记在内容区里面。
    标记成为内容的一部分,和文字一起缩进。
    所有文字都会相对于标记对齐。
  </li>
  <li class="list-item">第二项</li>
</ul>

list-style-position 的实际效果对比:

list-style-position: outside(默认)
┌──────────────────────────────────────┐
│ ● 标记在内容区外面                │
│   标记和第一行对齐                   │
│   第二行文字会缩进到标记位置           │
└──────────────────────────────────────┘

list-style-position: inside
┌──────────────────────────────────────┐
│   ● 标记在内容区里面                 │
│     标记成为内容的一部分               │
│     所有文字都相对于标记对齐           │
└──────────────────────────────────────┘

list-style-position 的实用场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/* 1. 带边框的列表 */
.bordered-list {
  list-style-position: outside;
  padding-left: 20px;
  border-left: 4px solid #3498db;
  background-color: #f8f9fa;
}

.bordered-list li {
  padding: 10px 0;
  border-bottom: 1px solid #eee;
}

.bordered-list li:last-child {
  border-bottom: none;
}

/* 2. 引用列表 */
.quote-list {
  list-style-position: inside;
}

.quote-list li {
  padding-left: 20px;
  text-indent: -20px;  /* 文字缩进到标记位置 */
  color: #555;
  font-style: italic;
  margin-bottom: 12px;
}

.quote-list li::before {
  content: "" "";  /* 使用引号作为标记 */
  color: #3498db;
}

/* 3. 任务清单 */
.task-list {
  list-style-position: outside;
}

.task-list li {
  position: relative;
  padding-left: 30px;
  margin-bottom: 8px;
}

.task-list li::before {
  content: "☐";  /* 未完成的任务 */
  position: absolute;
  left: 0;
  font-size: 18px;
}

.task-list li.completed::before {
  content: "☑";  /* 已完成的任务 */
  color: #2ecc71;
}
1
2
3
4
5
<ul class="task-list">
  <li>完成 CSS 教程第一章</li>
  <li class="completed">完成 CSS 教程第二章</li>
  <li>完成 CSS 教程第三章</li>
</ul>

💡 小技巧list-style-position: inside 在某些场景下很有用,比如你想要列表项的背景色包含标记。但要注意,inside 会让文字换行时缩进到标记位置,可能影响阅读体验。

16.1.3 list-style-image——用图片替代列表标记,不推荐,用 ::before 伪元素更灵活

list-style-image 允许你使用图片作为列表标记。但这个属性在现代 CSS 开发中已经不推荐使用了,因为 ::before 伪元素提供了更灵活的控制方式。

什么是 list-style-image

想象一下菜单上的菜品图片——用图片来替代简单的文字或符号。list-style-image 就是这个"菜品图片",只不过它替代的是列表的标记。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* list-style-image 的用法 */

.image-list {
  list-style-image: url("marker.png");  /* 使用图片作为标记 */
  /* 现代开发中不推荐这种写法 */
}

/* ⚠️ list-style-image 的问题 */

/* 问题1:图片大小不好控制 */
.problematic-list {
  list-style-image: url("small-icon.png");
  /* 图片太小或太大都不好调整 */
}

/* 问题2:图片位置不好控制 */
.problematic-list2 {
  list-style-image: url("marker.png");
  /* 图片和文字的对齐位置不好精确控制 */
}

/* 问题3:图片和标记类型需要分开设置 */
.mixed-list {
  list-style-type: square;              /* 还要设置一个备用类型 */
  list-style-image: url("marker.png"); /* 图片加载失败时显示方块 */
}

现代替代方案:使用 ::before 伪元素

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
/* 推荐:使用 ::before 伪元素替代 list-style-image */

.custom-marker-list {
  list-style: none;  /* 清除默认标记 */
  padding: 0;
}

.custom-marker-list li {
  position: relative;
  padding-left: 28px;  /* 给标记留出空间 */
  margin-bottom: 8px;
  line-height: 1.6;
}

.custom-marker-list li::before {
  content: url("marker.png");  /* 图片URL作为content */
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);  /* 垂直居中 */
  width: 20px;
  height: 20px;
  background-image: url("marker.png");
  background-size: contain;
  background-repeat: no-repeat;
}

/* 更灵活的版本:使用 emoji 或 SVG */
.emoji-list {
  list-style: none;
  padding: 0;
}

.emoji-list li {
  position: relative;
  padding-left: 28px;
  margin-bottom: 10px;
}

.emoji-list li::before {
  content: "👉";  /* 使用 emoji 作为标记 */
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
}

/* 箭头列表 */
.arrow-list {
  list-style: none;
  padding: 0;
}

.arrow-list li {
  position: relative;
  padding-left: 20px;
  margin-bottom: 8px;
}

.arrow-list li::before {
  content: "→";  /* 使用箭头符号 */
  position: absolute;
  left: 0;
  color: #3498db;
  font-weight: bold;
}

/* 使用 CSS 绘制的图标 */
.css-icon-list {
  list-style: none;
  padding: 0;
}

.css-icon-list li {
  position: relative;
  padding-left: 24px;
  margin-bottom: 12px;
}

.css-icon-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  width: 10px;
  height: 10px;
  background-color: #2ecc71;
  border-radius: 50%;  /* 圆点 */
}

.css-icon-list li.check::before {
  background-color: transparent;
  border: 2px solid #2ecc71;
  border-radius: 0;
  width: 12px;
  height: 6px;
  border-top: none;
  border-right: none;
  transform: rotate(-45deg);
  top: 8px;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<ul class="custom-marker-list">
  <li>使用背景图片的自定义标记</li>
  <li>可以精确控制大小和位置</li>
</ul>

<ul class="emoji-list">
  <li>👉 使用 emoji 作为标记</li>
  <li>🎯 灵活多变</li>
  <li>✨ 不需要额外图片资源</li>
</ul>

<ul class="arrow-list">
  <li>→ 箭头列表</li>
  <li>→ 简洁明了</li>
  <li>→ 易于维护</li>
</ul>

💡 小技巧:现代 CSS 开发中,强烈推荐使用 ::before 伪元素来替代 list-style-image。因为 ::before 可以让你完全控制标记的大小、位置、颜色,甚至可以添加多个图标或复杂的设计。

16.1.4 list-style——type position image 缩写

list-stylelist-style-typelist-style-positionlist-style-image 的缩写属性。使用缩写可以让代码更简洁。

list-style 的缩写语法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* 完整写法 */
.full-list-style {
  list-style-type: disc;
  list-style-position: outside;
  list-style-image: none;
}

/* 缩写写法 */
.shorthand-list-style {
  /* 顺序可以是任意的 */
  list-style: disc outside none;
  /* 或者 */
  list-style: none outside disc;
  /* 或者 */
  list-style: url("marker.png") inside square;
}
1
2
3
4
5
<ul class="shorthand-list-style">
  <li>使用 list-style 缩写的列表</li>
  <li>代码更简洁</li>
  <li>更易维护</li>
</ul>

list-style 缩写值的顺序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* list-style: <list-style-type> <list-style-position> <list-style-image> */

/* 常用组合 */

/* 无标记列表(最常用)*/
.no-marker {
  list-style: none;  /* 清除所有样式 */
  padding-left: 0;
}

/* 自定义图标 + 外部标记 */
.custom-outside {
  list-style: none;  /* 先清除默认 */
  padding-left: 24px;
}

.custom-outside li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: #2ecc71;
}

/* 带图片标记 */
.image-marker {
  list-style: square outside url("marker.png");
  /* 如果图片加载失败,显示 square */
}

/* 带内联标记 */
.inside-marker {
  list-style: decimal inside;
}

list-style 的实际应用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/* 1. 全局列表样式重置 */
ul, ol {
  list-style: none;  /* 清除默认样式 */
  padding-left: 0;
  margin: 0;
}

/* 2. 导航菜单 */
.nav-menu {
  list-style: none;          /* 无标记 */
  display: flex;              /* 水平排列 */
  gap: 24px;                 /* 间距 */
}

.nav-menu li a {
  text-decoration: none;
  color: #333;
  font-weight: 500;
  transition: color 0.2s;
}

.nav-menu li a:hover {
  color: #3498db;
}

/* 3. 文章目录 */
.article-toc {
  list-style: none;           /* 无标记 */
  padding-left: 0;
  border-left: 2px solid #eee;
}

.article-toc li {
  padding-left: 16px;
  margin-bottom: 8px;
  border-left: 2px solid transparent;
  transition: border-color 0.2s;
}

.article-toc li:hover {
  border-left-color: #3498db;
}

.article-toc li a {
  text-decoration: none;
  color: #555;
}

.article-toc li a:hover {
  color: #3498db;
}

/* 4. 面包屑导航 */
.breadcrumb {
  list-style: none;           /* 无标记 */
  display: flex;               /* 水平排列 */
  align-items: center;
  gap: 8px;
  font-size: 14px;
}

.breadcrumb li:not(:last-child)::after {
  content: "/";                /* 使用 / 分隔 */
  color: #999;
  margin-left: 8px;
}

.breadcrumb li a {
  text-decoration: none;
  color: #666;
}

.breadcrumb li:last-child {
  color: #333;
  font-weight: 500;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 导航菜单 -->
<ul class="nav-menu">
  <li><a href="/">首页</a></li>
  <li><a href="/products">产品</a></li>
  <li><a href="/about">关于</a></li>
  <li><a href="/contact">联系</a></li>
</ul>

<!-- 文章目录 -->
<ol class="article-toc">
  <li><a href="#section1">第一章:入门</a></li>
  <li><a href="#section2">第二章:进阶</a></li>
  <li><a href="#section3">第三章:高级</a></li>
</ol>

<!-- 面包屑导航 -->
<ol class="breadcrumb">
  <li><a href="/">首页</a></li>
  <li><a href="/products">产品</a></li>
  <li><a href="/products/electronics">电子产品</a></li>
  <li>智能手机</li>
</ol>

💡 小技巧list-style: none 是现代 CSS 开发中最常用的列表样式重置写法。配合 padding-left: 0 可以完全清除列表的默认样式,然后你就可以用 ::before 伪元素来添加任何你想要的标记样式了。

16.2 CSS 计数器

16.2.1 counter-reset——创建或重置计数器,如 counter-reset: my-counter;

CSS 计数器是一个强大但鲜为人知的功能。它允许你创建"虚拟计数器",并在列表项或其他元素中自动递增编号,而无需使用 JavaScript。

什么是 CSS 计数器?

想象一下一个自动编号机。你设置一个起始数字,然后每使用一次,数字就自动加一。CSS 计数器就是这个"自动编号机",只不过它是纯 CSS 实现的,不需要 JavaScript。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* counter-reset 的基本用法 */

/* 创建一个名为 my-counter 的计数器,起始值为 0 */
.counter-reset {
  counter-reset: my-counter;
}

/* 也可以创建多个计数器 */
.multiple-counters {
  counter-reset:
    section-counter    /* 章节计数器 */
    item-counter 5     /* 项目计数器,起始值为5 */
    figure-counter;     /* 图表计数器,默认起始值为0 */
}

/* counter-reset: none —— 取消计数器重置 */
.no-reset {
  counter-reset: none;  /* 不重置任何计数器 */
}

计数器的生命周期:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 计数器的作用域是整个元素及其后代 */

/* 在父元素上创建计数器 */
.counter-container {
  counter-reset: item-counter;  /* 创建计数器 */
}

/* 子元素可以使用这个计数器 */
.counter-container .item {
  /* 可以使用和递增 item-counter */
}

.counter-container .nested {
  /* 也可以使用 item-counter */
}

16.2.2 counter-increment——递增计数器,如 counter-increment: my-counter(默认每次递增 1)

counter-increment 用来递增(增加)计数器的值。默认情况下,每次递增 1,但你也可以指定其他增量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* counter-increment 的基本用法 */

.increment-default {
  counter-increment: my-counter;  /* 默认每次递增 1 */
}

/* 指定增量 */
.increment-2 {
  counter-increment: my-counter 2;  /* 每次递增 2 */
}

.increment-10 {
  counter-increment: step-counter 10;  /* 每次递增 10 */
}

/* 递减(使用负数)*/
.decrement {
  counter-increment: reverse-counter -1;  /* 每次递减 1 */
}

/* counter-increment: none —— 不递增 */
.no-increment {
  counter-increment: none;  /* 不递增任何计数器 */
}

16.2.3 counter()——使用计数器值,如 content: counter(my-counter);

counter() 函数用来获取计数器的当前值,通常配合 content 属性一起使用,在 ::before::after 伪元素中显示计数器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* counter() 的基本用法 */

.counter-item {
  counter-reset: my-counter;  /* 创建并重置计数器 */
}

.counter-item li {
  counter-increment: my-counter;  /* 每个li递增计数器 */
}

.counter-item li::before {
  content: counter(my-counter);  /* 显示计数器的值 */
  /* 显示效果:1, 2, 3... */
}

/* 计数器与列表的完美配合 */
.numbered-list {
  counter-reset: numbered-item;
  list-style: none;  /* 清除默认列表样式 */
  padding: 0;
}

.numbered-list li {
  counter-increment: numbered-item;
  padding-left: 40px;  /* 给计数器留出空间 */
  position: relative;
  margin-bottom: 12px;
}

.numbered-list li::before {
  content: counter(numbered-item);  /* 显示编号 */
  position: absolute;
  left: 0;
  top: 0;
  width: 28px;
  height: 28px;
  background-color: #3498db;
  color: white;
  border-radius: 50%;
  text-align: center;
  line-height: 28px;
  font-weight: bold;
  font-size: 14px;
}
1
2
3
4
5
<ol class="numbered-list">
  <li>这是第一个列表项,前面显示 1</li>
  <li>这是第二个列表项,前面显示 2</li>
  <li>这是第三个列表项,前面显示 3</li>
</ol>

16.2.4 counters()——嵌套计数器,如 content: counters(my-counter, “.”);(输出 1.2.3)

counters() 函数用于处理嵌套的计数器。比如一个多层嵌套的列表,每一层都需要显示 “1.2.3” 这样的编号,counters() 就能派上用场。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* counters() 的基本用法 */

.nested-counter-list {
  counter-reset: level-1;  /* 创建一个顶层计数器 */
  list-style: none;
  padding-left: 0;
}

.level-1-item {
  counter-increment: level-1;
  counter-reset: level-2;  /* 重置第二层计数器 */
}

.level-1-item::before {
  content: counter(level-1) ". ";
  font-weight: bold;
}

.level-2-item {
  counter-increment: level-2;
  padding-left: 40px;
}

.level-2-item::before {
  content: counter(level-1) "." counter(level-2) ". ";
  /* 显示效果:1.1, 1.2, 2.1... */
}

/* 使用 counters() 自动处理嵌套 */
.auto-nested {
  counter-reset: item;
  list-style: none;
  padding-left: 20px;
}

.auto-nested li {
  counter-increment: item;
}

.auto-nested li::before {
  content: counters(item, ".") " ";
  /* counters() 会自动处理嵌套级别 */
  /* 第一级:1, 2, 3... */
  /* 第二级(嵌套在1下):1.1, 1.2... */
  /* 第三级:1.1.1, 1.1.2... */
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<ol class="auto-nested">
  <li>第一章
    <ol>
      <li>第一章第一节</li>
      <li>第一章第二节
        <ol>
          <li>1.2.1 小节</li>
          <li>1.2.2 小节</li>
        </ol>
      </li>
    </ol>
  </li>
  <li>第二章
    <ol>
      <li>第二章第一节</li>
    </ol>
  </li>
</ol>

计数器的样式设置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* 计数器也可以设置样式 */

/* 设置编号的格式 */
.format-list {
  counter-reset: item;
  list-style: none;
}

.format-list li {
  counter-increment: item;
}

.format-list li::before {
  content: counter(item, upper-alpha);  /* 使用大写字母 */
  /* 显示效果:A, B, C... */
}

/* 可用的格式类型 */
/* decimal - 阿拉伯数字(1, 2, 3...)*/
/* decimal-leading-zero - 带前导零(01, 02...)*/
/* lower-alpha - 小写字母(a, b, c...)*/
/* upper-alpha - 大写字母(A, B, C...)*/
/* lower-roman - 小写罗马数字(i, ii, iii...)*/
/* upper-roman - 大写罗马数字(I, II, III...)*/
/* lower-greek - 小写希腊字母(α, β, γ...)*/

计数器的实际应用场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/* 1. 自动编号的文章章节 */
.article {
  counter-reset: section;
}

.article h2 {
  counter-increment: section;
  counter-reset: subsection;
}

.article h2::before {
  content: counter(section) ". ";
  color: #3498db;
  font-weight: bold;
}

.article h3 {
  counter-increment: subsection;
}

.article h3::before {
  content: counter(section) "." counter(subsection) " ";
  color: #666;
}

/* 2. 图片自动编号(图1、图2...)*/
.gallery {
  counter-reset: figure-num;
}

.gallery figure {
  counter-increment: figure-num;
}

.gallery figcaption::before {
  content: "图" counter(figure-num) ": ";
  font-weight: bold;
  color: #3498db;
}

/* 3. 脚注编号 */
.footnote-ref {
  counter-increment: footnote;
  font-size: 12px;
  color: #3498db;
  cursor: pointer;
}

.footnote-ref::before {
  content: "[" counter(footnote) "]";
}

.footnote {
  counter-increment: footnote;
  font-size: 12px;
  color: #666;
}

.footnote::before {
  content: "[" counter(footnote) "] ";
}

/* 4. 步骤指示器 */
.workflow {
  counter-reset: step;
  display: flex;
  gap: 20px;
}

.workflow-step {
  counter-increment: step;
  flex: 1;
  text-align: center;
  position: relative;
}

.workflow-step::before {
  content: counter(step);
  display: block;
  width: 40px;
  height: 40px;
  margin: 0 auto 10px;
  line-height: 40px;
  background-color: #3498db;
  color: white;
  border-radius: 50%;
  font-weight: bold;
}

💡 小技巧:CSS 计数器是一个被严重低估的功能。它可以实现很多需要 JavaScript 才能完成的编号功能,比如自动目录、章节编号、图片编号等。而且 CSS 计数器是纯 CSS 实现的,性能比 JavaScript 更好。

16.3 @counter-style 自定义计数样式

16.3.1 基本语法——@counter-style custom { system: numeric; symbols: “I” “II” “III”; }

@counter-style 是 CSS 提供的一个强大功能,它允许你定义自定义的计数样式。如果内置的计数类型(decimal、disc 等)不能满足需求,你可以通过 @counter-style 创建完全自定义的计数系统。

什么是 @counter-style?

想象一下你开了一家餐厅,需要用独特的编号方式来给菜品编号。内置的 “1, 2, 3…” 太普通了,你想用罗马数字,或者用emoji,或者用自定义符号。@counter-style 就是让你创建"自定义菜品编号系统"的工具。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* @counter-style 的基本语法 */

/* 定义一个自定义计数器样式 */
@counter-style custom-roman {
  /* 系统类型 */
  system: numeric;           /* 使用数字系统 */

  /* 符号定义 */
  symbols: "I" "II" "III" "IV" "V" "VI" "VII" "VIII" "IX" "X";
  /* 对应 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 */
}

/* 使用自定义计数器 */
.roman-list {
  list-style: custom-roman;
}
1
2
3
4
5
6
7
8
<ol class="roman-list">
  <li>第一章 - 介绍</li>
  <li>第二章 - 基础</li>
  <li>第三章 - 进阶</li>
  <li>第四章 - 高级</li>
  <li>第五章 - 实战</li>
</ol>
<!-- 显示为:I, II, III, IV, V... -->

@counter-style 的完整结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 定义一个更完整的自定义计数器 */
@counter-style emoji-counter {
  /* 系统类型 */
  system: fixed -1;  /* 从 -1 开始编号 */

  /* 符号定义 */
  symbols: "😀" "😁" "😂" "🤣" "😃" "😄" "😅";

  /* 前缀 */
  prefix: "";

  /* 后缀 */
  suffix: "、";
}

/* 使用 */
.emoji-list {
  list-style: emoji-counter;
}
1
2
3
4
5
6
<ul class="emoji-list">
  <li>开心的表情</li>
  <li>微笑</li>
  <li>大笑</li>
</ul>
<!-- 显示为:😀、😁、😂... -->

16.3.2 内置系统——cyclic(循环)、numeric(数字)、alphabetic(字母)、additive(加法)、fixed(固定符号)、extends(扩展内置)

@counter-style 提供了多种"系统"来定义计数规则,每种系统适合不同的场景。

1. cyclic(循环系统)——循环使用给定的符号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* cyclic 系统:循环使用符号列表 */
@counter-style cyclic-dots {
  system: cyclic;
  symbols: "●" "○" "◎";  /* 只有3个符号,循环使用 */
  /* 1=●, 2=○, 3=◎, 4=●, 5=○... */
}

.cyclic-list {
  list-style: cyclic-dots;
}

2. numeric(数字系统)——将数字转换为符号

1
2
3
4
5
6
7
8
9
/* numeric 系统:用符号表示数字 */
@counter-style dice {
  system: numeric;
  symbols: "⚀" "⚁" "⚂" "⚃" "⚄" "⚅";  /* 骰子点数 */
}

.dice-list {
  list-style: dice;
}
1
2
3
4
5
6
7
8
9
<ol class="dice-list">
  <li>摇到1点</li>
  <li>摇到2点</li>
  <li>摇到3点</li>
  <li>摇到4点</li>
  <li>摇到5点</li>
  <li>摇到6点</li>
</ol>
<!-- 显示为:⚀、⚁、⚂、⚃、⚄、⚅、⚀(循环)... -->

3. alphabetic(字母系统)——使用字母序列

1
2
3
4
5
6
7
8
9
/* alphabetic 系统:使用字母序列 */
@counter-style alpha-asterisk {
  system: alphabetic;
  symbols: "①" "②" "③" "④" "⑤";  /* 带圈数字 */
}

.alpha-list {
  list-style: alpha-asterisk;
}

4. additive(加法系统)——通过加法组合符号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* additive 系统:罗马数字使用加法 */
@counter-style roman-upper {
  system: additive;
  range: 1 3999;
  additive-symbols:
    1000 "M"
    900 "CM"
    500 "D"
    400 "CD"
    100 "C"
    90 "XC"
    50 "L"
    40 "XL"
    10 "X"
    9 "IX"
    5 "V"
    4 "IV"
    1 "I";
}

.roman-upper-list {
  list-style: roman-upper;
}

5. fixed(固定符号系统)——使用固定符号,超出后回退

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* fixed 系统:固定符号,超出后使用备选 */
@counter-style zodiac {
  system: fixed;
  symbols: "♈" "♉" "♊" "♋" "♌" "♍" "♎" "♏" "♐" "♑" "♒" "♓";  /* 12星座 */
  suffix: " ";
}

.zodiac-list {
  list-style: zodiac;
}
1
2
3
4
5
6
<ol class="zodiac-list">
  <li>白羊座</li>
  <li>金牛座</li>
  <li>双子座</li>
  <!-- ... -->
</ol>

6. extends(扩展系统)——扩展内置样式

1
2
3
4
5
6
7
8
9
/* extends 系统:扩展已有样式 */
@counter-style my-dots {
  system: extends disc;  /* 扩展 disc */
  suffix: ": ";          /* 修改后缀 */
}

.extended-list {
  list-style: my-dots;
}

16.3.3 negative——定义负数的符号,如 negative: “(” “)”;

negative 属性允许你定义负数的表示方式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 定义负数符号 */
@counter-style signed {
  system: numeric;
  symbols: "-0" "1" "2" "3" "4" "5";
  negative: "(" ")";  /* 负数用括号包裹 */
}

.signed-list {
  list-style: signed;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<ol class="signed-list">
  <li>正数项</li>
  <li>另一个正数项</li>
</ol>
<!-- 显示为:1, 2... -->
<!-- 如果需要负数,可以这样: -->
<ol class="signed-list" start="-3">
  <li>负数项</li>
</ol>
<!-- 显示为:(3), (2), (1)... -->

16.3.4 prefix / suffix——计数器前后缀,如 prefix: “第”;

prefixsuffix 属性用于添加计数器的前缀和后缀。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* 定义带前缀后缀的计数器 */
@counter-style chapter-style {
  system: numeric;
  symbols: "1" "2" "3" "4" "5";
  prefix: "第";     /* 前缀 */
  suffix: "章";     /* 后缀 */
  /* 显示效果:第1章、第2章、第3章... */
}

.chapter-list {
  list-style: chapter-style;
}

/* 常用组合 */
@counter-style section-style {
  system: numeric;
  symbols: "1" "2" "3" "4" "5";
  prefix: "§ ";
  suffix: " - ";
}

.section-list {
  list-style: section-style;
}

/* 图注样式 */
@counter-style figure-style {
  system: numeric;
  symbols: "1" "2" "3" "4" "5";
  prefix: "图 ";
  suffix: ": ";
}

.figure-list {
  list-style: figure-style;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<ol class="chapter-list">
  <li>CSS 基础</li>
  <li>选择器详解</li>
  <li>盒模型入门</li>
</ol>
<!-- 显示为:第1章、第2章、第3章... -->

<ol class="figure-list">
  <li>网站截图</li>
  <li>设计稿</li>
  <li>最终效果</li>
</ol>
<!-- 显示为:图 1:、图 2:、图 3:... -->

💡 小技巧@counter-style 是 CSS 中非常强大的功能,但浏览器支持情况不一。在使用前,请确认你的目标浏览器是否支持。必要的情况下,可以准备一个 fallback 方案。

16.4 quotes 引号属性

16.4.1 quotes——定义引号字符,如 quotes: “«” “»” “‹” “›”;

quotes 属性用于定义 <q> 元素(行内引用)使用的引号字符。这个属性可以让你自定义引号的样式,支持多级嵌套引用。

什么是 quotes 属性?

想象一下写作文时用的引号——第一层用"",第二层用’’,第三层又用""。quotes 属性就是定义这些引号"长什么样"的规则。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* quotes 的基本用法 */

/* 定义引号字符 */
.quote-style {
  quotes: "«" "»" "‹" "›";
  /* 第一个是开引号,第二个是闭引号 */
}

/* 使用 <q> 元素 */
.quote-style q {
  font-style: italic;
}
1
2
3
4
5
6
7
8
9
<p class="quote-style">
  老子说:<q>道可道,非常道</q></p>
<!-- 显示为:老子说:«道可道,非常道»。 -->

<p class="quote-style">
  老师说:<q>这句诗的意思是<q>长江后浪推前浪</q>的精神</q></p>
<!-- 嵌套引用显示为:老师说:«这句诗的意思是‹长江后浪推前浪‹的精神»。 -->

quotes 的语法详解:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* quotes 可以定义多级引号 */
/* 格式:quotes: 开1 闭1 开2 闭2 开3 闭3... */

/* 两级引号 */
.two-level {
  quotes: """ """";  /* 第一级用"",第二级用"" */
}

/* 三级引号 */
.three-level {
  quotes: "" "" "" "" "" "";
  /* 第一级用《》,第二级用「」,第三级用『』 */
}

/* 常用引号组合 */
.angle-quotes {
  quotes: "«" "»" "" "";
  /* 西文尖引号,最常用 */
}

.double-quotes {
  quotes: "" "" "" "";
  /* 英文双引号 */
}

.chinese-quotes {
  quotes: "" "" "" "";
  /* 中文引号 */
}

.single-quotes {
  quotes: "'" "'";
  /* 单引号 */
}

/* none —— 不显示引号 */
.no-quotes {
  quotes: none;
}

.no-quotes q::before {
  content: none;  /* 不显示任何内容 */
}

quotes 的实际应用场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/* 1. 西方风格的引用 */
.western-quote {
  quotes: """ """" "" "";
}

.western-quote q {
  font-style: italic;
}

.western-quote q::before {
  content: open-quote;  /* 使用 open-quote */
  color: #3498db;
}

.western-quote q::after {
  content: close-quote;  /* 使用 close-quote */
  color: #3498db;
}

/* 2. 中文风格的引用 */
.chinese-quote {
  quotes: """ """ "" "";
}

.chinese-quote q {
  color: #555;
}

.chinese-quote q::before {
  content: open-quote;
  margin-right: 0.2em;
}

.chinese-quote q::after {
  content: close-quote;
  margin-left: 0.2em;
}

/* 3. 多级嵌套引用 */
.nested-quote {
  quotes: "" "" "" "" "" "";
}

.nested-quote q {
  color: #333;
}

.nested-quote q q {
  color: #666;
  font-size: 0.9em;
}

/* 4. 自定义引号样式 */
.fancy-quote {
  quotes: "" "" "" "❜";
}

.fancy-quote q {
  font-family: Georgia, serif;
  font-style: italic;
}

.fancy-quote q::before {
  content: open-quote;
  font-size: 1.5em;
  color: #e74c3c;
  vertical-align: -0.3em;
  margin-right: 0.1em;
}

.fancy-quote q::after {
  content: close-quote;
  font-size: 1.5em;
  color: #e74c3c;
  vertical-align: -0.3em;
  margin-left: 0.1em;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- 西方风格引用 -->
<p class="western-quote">
  莎士比亚说:<q>To be, or not to be, that is the question</q></p>
<!-- 显示为:莎士比亚说:<To be, or not to be, that is the question>。 -->

<!-- 中文风格引用 -->
<p class="chinese-quote">
  孔子说:<q>学而时习之,不亦说乎</q></p>
<!-- 显示为:孔子说:「学而时习之,不亦说乎」。 -->

<!-- 多级嵌套 -->
<p class="nested-quote">
  作者说:<q>这就是<q>嵌套引用</q>的效果</q></p>
<!-- 显示为:作者说:(这就是【嵌套引用】的效果)。 -->

open-quoteclose-quote 特殊值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/* open-quote 和 close-quote 是特殊的 CSS 关键字 */

/* open-quote —— 插入开引号 */
.open-quote-example::before {
  content: open-quote;  /* 使用 quotes 定义的开引号 */
}

/* close-quote —— 插入闭引号 */
.close-quote-example::after {
  content: close-quote;  /* 使用 quotes 定义的闭引号 */
}

/* no-open-quote —— 插入但不增加嵌套层级 */
.no-nest-example::before {
  content: no-open-quote;  /* 不显示引号,但层级增加 */
}

/* no-close-quote —— 同理 */
.no-nest-example::after {
  content: no-close-quote;
}

quotes 与语言的关系:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/* 可以根据语言自动应用不同的引号风格 */

/* 英文页面 */
:lang(en) {
  quotes: """ """" "" "";
}

/* 法文页面 */
:lang(fr) {
  quotes: "« " " »" "" "";
}

/* 德文页面 */
:lang(de) {
  quotes: "" "" "" "";
}

/* 中文页面 */
:lang(zh) {
  quotes: """ """ 「」『」;
}

💡 小技巧quotes 属性在现代网页中使用得不多,因为大多数开发者更倾向于直接用 CSS 绘制引号(如 ::before { content: "«"; })。但 quotes 属性配合 <q> 元素使用,可以让代码更语义化,也更容易维护。


本章小结

恭喜你完成了第十六章的学习!让我们来回顾一下这章的精华:

核心知识点

属性说明
list-style-type列表标记类型(disc、circle、square、decimal、none等)
list-style-position标记位置(inside/outside)
list-style-image图片标记(不推荐,用::before更灵活)
list-style列表样式缩写
counter-reset创建/重置计数器
counter-increment递增计数器
counter()获取计数器值
counters()嵌套计数器
@counter-style自定义计数样式
quotes定义引号字符

列表样式对比

graph LR
    A["list-style-type"] --> B["disc(实心圆)"]
    A --> C["circle(空心圆)"]
    A --> D["square(方块)"]
    A --> E["decimal(数字)"]
    A --> F["none(无标记)"]

计数器工作流程

graph TD
    A["计数器工作流程"] --> B["1. counter-reset 创建计数器"]
    B --> C["2. counter-increment 递增"]
    C --> D["3. counter() 获取值"]
    D --> E["4. content 显示"]

实战建议

  1. 自定义列表标记:使用 list-style: none 清除默认样式,配合 ::before 伪元素添加自定义图标
  2. 自动编号:使用 CSS 计数器实现章节编号、图片编号等
  3. 嵌套编号:使用 counters() 处理多层嵌套列表
  4. 自定义引号:使用 quotes 属性配合 <q> 元素

下章预告

下一章我们将学习图片与替换元素属性,看看如何用 CSS 来控制图片和替换元素的显示效果!