LabHub
学习 学习路径 课程

CSS — 后写的规则获胜

算出胜出的那条规则

在 LabHub 中继续学习

目标

CSS 看起来不可预测,不是因为没有规则,而是规则分为三个阶段:重要性 → specificity → 顺序。

这里没有浏览器,因此不会评分“视觉效果是否正确”。我们会实际计算哪条规则获胜——即使没有浏览器,答案也只有一个。

要创建的内容

只需创建 /root/work/css/styles.css,不需要 HTML。 评分器会假设存在以下元素并计算 cascade。

div#app > div.card
main.lessonLayout > aside.lessonSide
main.lessonLayout.sideHidden > aside.lessonSide
div.toolbar

计算器

cd /root/work/css
python3 /opt/lab/checks/css-lab/cascade.py styles.css

它会显示每条规则的顺序、媒体条件与 specificity。支持标签、类、ID、伪类、后代与子代组合器、逗号分组,以及 @media (max-width|min-width: Npx)。遇到其他选择器时,不会静默跳过,而会明确提示“无法解析”。

步骤

  1. 3 个 :root token + 3 处 var()
  2. 预测 specificity → 02-spec.txt
  3. 不使用 !important 也能获胜
  4. 顺序陷阱(把默认规则放在媒体查询上方
  5. 三列网格
  6. 折叠后变为一列
  7. flex 工具栏
  8. 运行计算器 → 08-cascade.txt
  9. 总结 → 09-notes.md

参考

第 4 步和第 6 步都是该服务真实发生过的缺陷:移动端目录遮罩无法打开,以及折叠目录后页面增长到 33,000px。

把 token 放在 :root 中

创建 /root/work/css/styles.css,在 :root 中定义至少三个自定义属性,再通过 var()至少三处使用它们。

mkdir -p /root/work/css。名称可以自由选择,如 --accent--gap--fg。与 SCSS 变量不同,自定义属性在运行时仍然存在并会继承,因此可以只在某棵子树中覆盖。

计算 specificity

把下面五个选择器的 specificity 逐行写入 02-spec.txt,格式为 번호=a,b,c

1) a
2) .btn.primary
3) #nav a:hover
4) ul > li::marker
5) :where(.card) .title

a=ID,b=类、属性与伪类,c=标签与伪元素。例如 1=0,0,1。第 5 项是陷阱::where() 的 specificity 始终为 0。还要记住各位不会进位:11 个类也无法胜过 1 个 ID。

不使用 !important 也能获胜

先写 .card { color: ... },再让 #app 内的 .card 使用另一种颜色。不能使用 !important

评分器会实际计算 div#app > div.card 元素上哪条规则获胜。#app .card1,1,0,会胜过 .card0,1,0)。!important 只能由另一个 !important 击败,一旦使用,以后就必须一直这样处理该属性。

避开顺序陷阱

.lessonSideposition 默认是 sticky,在 max-width: 900px 时变为 fixed。两条规则 specificity 相同,因此顺序决定答案。

把默认规则放在 @media上方。若放在下方,因为 specificity 相同(都是 0,1,0),后写的默认规则在移动端也会获胜。评分器会分别在 1280px 与 500px 宽度计算。该服务的移动端遮罩无法打开,正是由这个缺陷导致。

用网格构建骨架

.lessonLayout 设为 display: grid,并指定三列grid-template-columnsgap

例如 grid-template-columns: 240px 1fr 200px。需要同时定义行与列时使用 grid;若只是在一行内分配空间,flex 更合适。

折叠时列也必须消失

.lessonLayout.sideHidden 时让布局变为一列。不能像 0 1fr 200px 那样留下宽度为 0 的列。

子元素为 display: none 时,会从网格中完全移除。若只把列设成 0,正文会被挤进 0px 列,导致每个字符都换行;真实页面曾因此增长到 33,000px。请减少列的数量,使用 grid-template-columns: 1fr

工具栏使用 flex

.toolbar 设为 display: flex,并同时指定 justify-contentalign-itemsgap

最常见的组合是 space-between + center + gap。若只想把一个元素推到右侧,也可以给它设置 margin-left: auto

用计算器确认

使用 /opt/lab/checks/css-lab/cascade.py 解析样式表,并把结果保存到 08-cascade.txt

python3 /opt/lab/checks/css-lab/cascade.py styles.css | tee 08-cascade.txt。输出会显示每条规则的顺序、媒体条件与 specificity。这正是看到开发者工具中带删除线规则时,大脑所做的计算。

解释为何失败

09-notes.md 中至少写三行:第 4 步颠倒顺序会发生什么,第 6 步保留 0 宽列会发生什么,以及想使用 !important 时应先检查什么。

正文中必须包含 특정성순서그리드。诊断始终分四步:选择器是否匹配、规则是否正在失败、原因是 specificity 还是顺序、是否错误期待继承。