本教程将展示如何使用CSS网格布局来轻松实现自定义列表的造型,此外:
- 将数据属性作为伪元素的内容
- 用于有序列表样式设计的CSS计数器
- 用于每个列表项造型的CSS自定义变量
- 响应式多列列表
更新:
::marker伪选择器现在在现代浏览器中得到了良好的支持。虽然本教程包括上述项目的方便的CSS提示,但你可能想跳到::marker。
列表HTML
首先,我们将设置我们的HTML,有一个ul 和一个li 。我包括了一个较长的子弹,以帮助检查对齐方式、间距和行距(line-heihgt):
<ul role="list">
<li>Unordered list item</li>
<li>Cake ice cream sweet sesame snaps dragée cupcake wafer cookie</li>
<li>Unordered list item</li>
</ul>
<ol role="list">
<li>Ordered list item</li>
<li>Cake ice cream sweet sesame snaps dragée cupcake wafer cookie</li>
<li>Ordered list item</li>
</ol>
注意使用role="list" 。起初,它可能看起来是多余的,但我们要用CSS去除固有的列表样式。虽然CSS通常不影响元素的语义价值,但list-style: none ,对于一些屏幕阅读器来说,可以删除列表语义。最简单的修复方法是定义role 属性来恢复这些语义。你可以从Scott O'Hara的这篇文章中了解更多。
基本列表CSS
首先,除了将它们定义为有间隙的网格外,我们还添加了一个重置的列表样式:
ol,
ul {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: 1rem;
}
grid-gap 的好处是在li 之间增加了空格,取代了旧的方法,如li + li { margin-top: ... } 。
接下来,我们要准备列表项:
li {
display: grid;
grid-template-columns: 0 1fr;
gap: 1.75em;
align-items: start;
font-size: 1.5rem;
line-height: 1.25;
}
我们也已经将列表项设置为使用网格。我们还升级了一个老的 "黑客 "方法,即使用padding-left 为绝对位置的 pseduo 元素留出空间,其组合是0 宽度的第一列和grid-gap 。我们稍后会看到它是如何工作的。然后我们使用align-items: start ,而不是默认的stretch ,并应用一些类型样式。
UL: 表情符号子弹的数据属性
现在,这可能不完全是一个可扩展的解决方案,但为了好玩,我们要添加一个自定义的数据属性,它将定义一个表情符号作为每个列表项的子弹。
首先,让我们更新一下ul HTML:
<ul role="list">
<li data-icon="🦄">Unordered list item</li>
<li data-icon="🌈">
Cake ice cream sweet sesame snaps dragée cupcake wafer cookie
</li>
<li data-icon="😎">Unordered list item</li>
</ul>
为了将表情符号用作子弹,我们使用了一种相当神奇的技术,即数据属性可以作为伪元素的content 属性的值。
ul li::before {
content: attr(data-icon);
/* Make slightly larger than the li font-size
but smaller than the li gap */
font-size: 1.25em;
}
下面是结果,检查了::before 元素,以帮助说明网格是如何工作的。

表情符号仍然被允许占用宽度来显示,但有效地位于网格间隙中。你可以尝试将第一个li 网格列设置为auto ,这将导致网格间隙完全应用于表情符号列和列表文本列之间。
OL:CSS计数器和CSS自定义变量
自IE8以来,CSS计数器一直是一个可行的解决方案,但我们要增加一个额外的亮点,即使用CSS自定义变量来改变每个数字的背景颜色。
我们将首先应用CSS计数器的样式,将我们的计数器命名为orderedlist 。
ol {
counter-reset: orderedlist;
}
ol li::before {
counter-increment: orderedlist;
content: counter(orderedlist);
}
这样就达到了以下效果,看起来与默认的ol 样式没有什么区别:

接下来,我们可以给计数器的数字应用一些基本样式:
/* Add to li::before rule */
font-family: "Indie Flower";
font-size: 1.25em;
line-height: 0.75;
width: 1.5rem;
padding-top: 0.25rem;
text-align: center;
color: #fff;
background-color: purple;
border-radius: 0.25em;
首先,我们应用谷歌字体,并将font-size 。line-height 是应用的line-height 的一半,li (至少这是对这种字体的效果,可能有点神奇的数字)。它使数字与主要的li 文字内容保持一致,这就是我们想要的。
然后,我们需要指定一个明确的宽度。如果不这样做,即使文本会出现,背景也不会出现。
添加填充物是为了固定文本与背景的对齐。
现在我们有了这个:

这当然是更多的自定义感觉,但我们将通过把background-color 换成一个CSS自定义变量来进一步推动它,像这样:
ol {
--li-bg: purple;
}
ol li::before {
background-color: var(--li-bg);
}
在我们为第二个和第三个li 添加内联样式以更新变量值之前,它将看起来是一样的:
<ol role="list">
<li>Ordered list item</li>
<li style="--li-bg: darkcyan">
Cake ice cream sweet sesame snaps dragée cupcake wafer cookie
</li>
<li style="--li-bg: navy">Ordered list item</li>
</ol>
这是最后的ul 和ol ,都放在一起了。
作者:Stephanie Eckles (@5t3ph)
升级你的算法多栏列表
我们的例子只有3个短的列表项,但别忘了我们在基础ol 和ul 上应用了网格。
而在前世,我在PHP中用modulus做了一些有趣的事情,将列表分割开来,并应用额外的类来实现均匀分割的多列列表。
有了CSS网格,你现在可以在三行中应用它,具有固有的响应性、平等的列和对内容行长度的尊重:
ol,
ul {
display: grid;
/* adjust the `min` value to your context */
grid-template-columns: repeat(auto-fill, minmax(22ch, 1fr));
gap: 1rem;
}
应用到我们现有的例子中(一定要先删除li 上的max-width ),可以得到。

你可以通过将Codepen中的$multicolumn 变量更新为true 来切换这个视图。
陷阱:超过纯文本作为li 内容
如果你在li 内有超过纯文本的内容--包括像一个无辜的<a> --我们的网格模板就会中断。
然而,这是个很容易解决的问题--把li 的内容包在一个span 。我们的网格模板并不关心这些元素是什么,但它只希望有两个元素,其中的伪元素算作第一个。
升级到CSS标记
在本文最初发布后的几个月里,所有现代浏览器对::marker 的支持都变得更好了。
::marker 伪选择器可以直接改变和设计ol 或ul 列表的子弹/数字的样式。
我们可以用::marker 完全取代ul 子弹的解决方案,但我们必须降低ol 的解决方案,因为::marker 只允许有几个属性:
animation-*colorcontentdirectionfont-*transition-*unicode-bidiwhite-space
无序列表样式与::marker
由于content 仍然是一个允许的属性,我们可以保留我们的data-icon 解决方案,以允许自定义表情符号 🎉。
我们只需要把::before 换成::marker 。
ul li::marker {
content: attr(data-icon);
font-size: 1.25em;
}
然后从li 中删除不再需要的网格属性,并重新添加一些padding ,以取代被删除的grid-gap 。
li {
/* replace the grid properties with: */
padding-left: 0.5em;
}
最后,我们之前删除了margin ,但我们需要重新添加一些左边的空白,以确保::marker 的空间,防止它因溢出而被切断。
/* update in existing rule */
ol,
ul {
margin: 0 0 0 2em;
/* ...existing styles */
}
而视觉效果和我们之前的解决方案是一样的,你可以在演示中看到。
使用::marker#的有序列表样式
对于我们的有序列表,我们现在可以切换并利用内置的计数器。
我们还必须放弃我们的background-color 和border-radius ,所以我们将换成使用我们的自定义属性的color 值。为了清楚起见,我们将把我们的自定义属性名称改为--marker-color 。
因此,我们减少的样式如下:
ol {
--marker-color: purple;
}
li::marker {
content: counter(list-item);
font-family: "Indie Flower";
font-size: 1.5em;
color: var(--marker-color);
}
不要忘了在HTML中也要更新CSS的自定义属性名称:
注意这个问题:把
display属性改成li将会删除::marker伪元素。因此,如果你需要一个不同的列表内容显示类型,你需要通过嵌套一个额外的包装元素来应用它。
::marker 演示#
这是我们更新的自定义列表样式,现在使用::marker 。
请务必检查当前浏览器的支持情况,根据你独特的受众决定使用哪种自定义列表样式方案你可能想选择使用::marker ,作为对之前展示的解决方案之一的逐步增强。