前言
ul > li 经常会用到, 它原本的 style 很丑, 这篇介绍如果修改它.
以前学 W3Schools 的时候也有记入过: HTML – W3Schools 学习笔记
参考:
Youtube – HTML & CSS for Beginners Part 17: How to Create and Style HTML Lists
Youtube – Styling your list-items just got so much easier with this pseudo-element!
Youtube – Next-Level List Bullets With CSS ::marker
Custom bullets with CSS ::marker
HTML 默认
<div class="container"> <ul> <li>item1</li> <li>item2</li> </ul> </div>
效果
红框是 container 范围. 整个 ul 默认有一个 padding-left 40px.
如果有写 reset CSS 的话, 那么它的 padding 通常是 0
bullet 会神奇的跑出去...
list-style-type
常见的 type 有: circle, alpha, roman, decimal
甚至可以直接写 string 哦
li { list-style-type: "> "; }
效果
list-style-position
它的功能是设置 bullet 是在 inside 还是 outside, 默认是 outside.
padding-left: 40px 的长相:
padding-left: 0 的长相:
inside 有个体验问题, 第 2 行的字没有和第一行对齐, 反而跑到了 bullet 的区域, 这样很不整齐..
Tailwind CSS 的解释图, 非常清晰:
Custom Styling
use Flex
设置 list-style-type: none. 在 HTML/CSS 写 icon 用 Flex 做间距.
.container { border: 1px solid black; width: fit-content; min-width: 200px; padding: 0.5rem; ul { list-style-type: none; li { &::before { font: var(--fa-font-solid); content: "\f00c"; padding-right: 0.25rem; } } } }
效果
用 Flex 的好处是直观, 最大的问题是 align-items. Flex 只能 center 或者 baseline, 没用办法做法 vertical-align: middle
当 bullet 和 text 的大小不一致又有 2 行 text 时, 就会有麻烦了.
除非我们知道 line-height 那就可以在 icon wrap 一层居中 icon. 但 hardcode line-height 不顺风水, 不同 font-family line-height 是不太一样的, lh unit 目前又还不支持.
目前无解.
use pseudo background image
参考: codepen example
li :: before 这种做法在 text 有 2 行的时候, 第 2 行会在 icon 下面. 解决方法是定位 (但是这个很不顺风水)
use ::marker
如果只是想换 icon 用 build-in 的 ::marker 会很方便, 虽然它没有 cover 100% 场景 (不支持 margin, padding), 但是通常够用了.
ul { font-size: 2rem; li { padding-left: 0.5rem; } ::marker { color: red; font: var(--fa-font-solid); content: "\f00d"; } }
marker 可以选中 bullet, 然后就可以自定义内容了. 上面用了 fontawesome 的 code. 效果:
也可以换成 text, url(svg..) 等等
::marker { color: red; content: "> " counter(list-item); }
还可以配上 counter 输出 index 哦.
它有 2 个问题
1. svg sprite 不行, 还有 svg 的 width, height 无法通过 font-size 控制 (即使在 svg 里写 1em 也没用, 可能是 ::marker 搞的鬼吧), 总之理想的做法是用 fontawesome
2. 当 icon 和 text 大小不一致时, 需要把 text 用 span wrap 起来然后设置 vertical-align: middle
注: 这个操作会导致 line-height 变大到 1.4 左右哦,
而且必须是 display: inline, inline-block 会变成 align center (like Flex), block 会变成 baseline
stackoverflow – Vertically align smaller bullets with larger text
use list-style-image
最大的问题是 svg 的大小无法用 CSS 控制. 只能写死在 svg 里
注意事项
1. icon, text 大小不一致时要 vertical-align 或 aligh-items
2. Flex, icon 要 flex-shrink:0 当遇到 2 行时它无法像 vertical-align 那样居中
3. svg 不管用 ::marker, ::before, list-style-image 都不顺, 尽可能用 fontawesome, 真的没办法就用 HTML svg + Flex 方案
4. 最佳方案是 ::marker 配 fontawesome, icon text 大小不一致时需要 vertical-alight: middle
间距问题
有 2 个间距挺烦人的,
第 1 个是 bullet 和 container 的间距.
ul 默认 position outside, bullet 不占据空间, 它依靠 padding-left 40px 把 bullet 弄出来.
但是这个 padding-left 不容易控制, 通常放 1.2em, 2ch, 或者 same font-size with bullet, 如果是字, 比如 "Step 1:" 用 ch 比较准. 但也只是大概准而已.
它的关键就在于 bullet 不占位子. 得靠 padding, 而这个 padding 就依赖 bullet 的大小, 然后只可以大概调.
如果用 inside 的话, 它就会刚刚好, 但是 second line 就对不齐了
我本来以为, 自己搞 Flex 就可以搞定这个对齐的问题. 后来我发现并不那么容易.
假如我们用 icon 替代 bullet, 然后让 li > icon + text, 在 li 做 Flex.
这个时候 vertical align 就难搞了. 这个 bullet 小于 text, 它又在 text 的中心, 这个是 vertical-align: middle 才做的到. 而 flex 只能 align-items: baseline.
除非我们知道 line-height 那就可以在 icon wrap 一层居中 icon. 但 hardcode line-height 不顺风水, 不同 font-family line-height 是不太一样的, lh unit 目前又还不支持.
第 2 个是 bullet 和 text 的间距.
这个是通过 li padding-left 控制的. ::marker 是写不到 padding 和 margin 的哦.
也有人通过 content: '\空格' 来实现. .
相关参考:
stackoverflow – How to keep indent for second line in ordered lists via CSS?
CSS – Indenting the second line of LI (List Items)
description list (dl > dt + dd)
<dl> <dt>Coffee</dt> <dd>Black hot drink</dd> <dt>Milk</dt> <dd>White cold drink</dd> </dl>
默认样式
它会一左一右是因为有 margin
但这种一高一低一左一右, 真的很丑. 所以很多人都问怎样让它一左一右就好. 类似 key value pair
stackoverflow – How to style dt and dd so they are on the same line?
首先是 standard 的 reset CSS, 去掉原本的 margin
* { margin: 0; padding: 0; box-sizing: border-box; }
然后用 Flex 就可以了
dl { display: flex; flex-wrap: wrap; row-gap: 1rem; dt { flex: 0 0 50%; } dd { flex: 0 0 50%; } }
其原理是让每个 item 占据 50%, 那么 1 row 最多只能容纳 2 个 item, 也就是 1 个 dt 和 1 个 dd.
然后剩余的就 wrap 往下掉. 再做一个 row-gap 让 row 有 spacing 就可以了
Grid 的写法也是更简单
dl { display: grid; grid-template-columns: 1fr 1fr; row-gap: 1rem; }
Float 的写法
dt, dd { &:nth-child(n + 3) { margin-top: 1rem; } 50%; float: left; }