伪元素是「不写进 HTML 的装饰盒子」,它的价值在于内容与结构无关

要点只有一条:伪元素的内容对辅助技术不可靠、对搜索与复制不可用。所以只放装饰(图标、线条、序号),不要放真实信息。现代浏览器会按 alt 般的规则处理 content 文本,但把它当「可见但不可选」的文字最安全。

① 两个最常用的位置

::before 加前缀图标 ::before 绝对定位画一条竖线,比 border-left 更可控(能设高度与圆角)

.quote { position: relative; padding-left: 14px; }
.quote::before { content: ""; position: absolute; left: 0; top: .2em; bottom: .2em; width: 3px; background: #0969da; }

单个冒号 :before 与双冒号 ::before 都能工作;规范用双冒号区分「伪元素」与「伪类」(单冒号)。

② content 的取值形态

字符串拼接 attr() 读属性 未标注时的回退  url() 图片 open-quote / close-quote

.x::before { content: "文字"; }                      /* 字符串 */
.x::before { content: attr(data-icon) " "; }         /* 属性值,可拼接 */
.x::after  { content: "(来源:" attr(data-src, "未标注") ")"; }  /* attr() 有第二个参数=回退值 */
.x::after  { content: url(arrow.svg); }              /* 图片 */
.x::before { content: counter(n) "." counter(m); }   /* 计数器 */
.x::before { content: open-quote; }                  /* 引号:跟随 lang 与 quotes 属性 */
.x::before { content: "\201C 前引号可以用 Unicode 转义"; }   /* 数字后要有空格分隔 */
.x::before { content: none; }                        /* 不生成盒子;content: "" 生成空盒子 */
/* 关键:::before/::after 只有在 content 被声明(哪怕是 "")时才会存在 */

③ counter():给章节做自动编号

第一节标题

小节一行,编号是「章.节」拼出来的

另一小节

第二节标题,末尾的引用会带上当前计数值

body { counter-reset: sect sub; }        /* 声明计数器(初值 0) */
.sect { counter-increment: sect; }        /* 递增 */
.sect::before { content: "第 " counter(sect) " 节:"; }
.sub { counter-increment: sub; }
.sub::before { content: counter(sect) "." counter(sub); }   /* 拼两级编号 */

/* 嵌套计数器:counter-reset 写在父级上就会自动嵌套,同一名字在子树里重新起算
   样式可选:counter(x, upper-roman) / lower-alpha / cjk-decimal / decimal-leading-zero */

有序列表 <ol start> 与 list-style 已够用;counter() 的价值在「多级编号 + 非列表元素 + 跨元素拼字符串」这类真实需求(论文式标题、题目序号)。

④ 其它伪元素:一个比一个更「只能装饰」

::first-line 让这一整行变粗,字号与宽度决定「第一行」的内容。::first-letter 让首字变大变红,杂志风就靠这两个。

选中这段文字试试:::selection 改了高亮底色。别用低对比配色,那是可访问性缺陷。

::selection { background: #bff5d1; color: #0a3d1f; }
/* 只允许 color / background / text-shadow / decoration 等少数属性 */

⑤ 五个纯 CSS 装饰配方

  1. 圆形序号徽标(counter + display: inline-grid 居中)
  2. 第二步自动是 2

右上角红点 悬停:下划线从左展开 外链尾标

尖角 tooltip(border 三角形)

/* 骨架屏:一条 background-position 动画,不需要图片 */
.skeleton { background: linear-gradient(90deg, #eaeef2 0 25%, #f6f8fa 37%, #eaeef2 63%) 0 0/400% 100%;
            animation: sk 1.4s ease infinite; }
/* 尖角:零尺寸盒子 + 四条透明 border,只给一侧上色 */
.tip::after { content: ""; border: 6px solid transparent; border-top-color: #24292f; position: absolute; top: 100%; }
/* hover 下划线:动 ::after 的 scaleX,不动 text-decoration(后者不能平滑) */
.ul-anim::after { transform: scaleX(0); transform-origin: left; transition: transform .22s; }
.ul-anim:hover::after { transform: scaleX(1); }

⑥ 三个必须知道的限制

限制说明与对策
不能嵌 HTMLcontent 里写 <b> 只会显示字面量。需要结构就写进 DOM。
不可选中、不可搜索、复制丢失所以「必需信息」不要只存在于伪元素里(例如价格、状态文字)。读屏支持参差不齐。
每个元素只有两个需要三层装饰时:给父用 ::before、子元素用 ::after,或改用真实 <span>、多重 box-shadow、多层 background(第八章)。

content 与 alt 的取舍:图片的说明文字属于 HTML(alt),因为它是内容;图标字体/装饰符号属于 CSS,因为它是表现。