必须用而非,因其天然语义化、支持屏幕阅读器读序号、便于精准选择当前项;主线用::before+position: relative定位,圆点用::after+top: 50%+transform translateY(-50%)垂直居中,确保内容变动时布局不崩。

直接用 <ol></ol> 套 <li>,主线画在容器上、圆点画在每个 <li> 上,别用 flex 或 grid 堆中轴线——这是最稳的竖向时间轴布局方式,改内容、加动画、切深色模式都不容易崩。
为什么必须用 <ol></ol> 而不是 <div> 套 <code><div>
<p>语义对了,屏幕阅读器能读出“第1项、第2项”,<code>datetime 属性也能被搜索引擎和无障碍工具识别;更重要的是,后续想加“当前进行中”状态,可以用 ol > li:nth-child(3) 精准命中,不用靠 class 切换或 JS 查找。常见错误是写成 <div class="timeline"><div class="item">...</div></div>,这种结构一加 hover 动画或 IntersectionObserver 就得反复改选择器。
::before 画主线时,position: absolute 必须配 position: relative 在父容器上
::before 画主线时,position: absolute 必须配 position: relative 在父容器上否则伪元素会相对于 定位,主线偏移、断开、随滚动错位全由此起。实操要点:
-
<ol class="timeline"></ol>必须设position: relative -
.timeline::before设content: ""、position: absolute、left: 50%、transform: translateX(-50%)、width: 2px、height: 100% - 漏掉
position: relative,或者把transform写成translateY(-50%)(这是垂直居中圆点用的),主线就会歪到左边或消失
每个时间点的圆点必须用 ::after + top: 50%; transform: translateY(-50%)
不是 margin-top 推,也不是 line-height 挤,更不能用 vertical-align(它对块级元素无效)。关键原因:
立即学习“前端免费学习笔记(深入)”;
-
<li>内容高度不固定(比如文字换行),只有top: 50% + translateY(-50%)能保证圆点始终钉在整块内容的垂直中心 - 如果用了
margin-top: 20px,字体一变、行数一增,圆点就卡在第一行末尾,视觉上严重错位 -
<li>必须设position: relative,否则::after的top会相对于<ol></ol>计算,导致所有圆点叠在一起
左右交替布局别用 float 或硬写 left/right,用 :nth-child(odd/even) 控制 padding 和 text-align
移动端缩放、字体加载延迟、RTL 语言(如阿拉伯语)都会让 float 失效或翻车。正确做法:
-
.timeline li:nth-child(odd) .timeline-content设text-align: right、padding-right: 80px -
.timeline li:nth-child(even) .timeline-content设text-align: left、padding-left: 80px - 圆点统一用
left: 50%+transform: translateX(-50%),不随左右变化而重写定位逻辑
真正难搞的不是画出来,而是当某条内容突然多出三行文字、或用户开了系统级大字体、或切换深色模式时,圆点还稳稳钉在内容中间,主线不断不偏——这得靠伪元素 + 相对定位 + CSS 变量三者咬死,少一个环节都容易露馅。



















