写给「自己写得少、看别人代码多」的人。重点不是教你从零搭页面,而是:当你在别人的代码里看到某个写法,你知道它是什么、为什么这么写、它会带来什么效果。
顺序大致从「天天见」到「认识就行」。
一、最该牢固的,其实是层叠(Cascade)
读 CSS 最容易踩坑的不是某个新属性,而是「为什么我改的样式不生效」。答案几乎都在这三件事里:
1. 特异性(Specificity) 谁更具体谁赢。粗略记法是三个档位,从高到低:
#id(最强).class/[attr]/:hover这类伪类- 标签名
div/ 伪元素::before
!important 凌驾于一切之上,所以代码里满屏 !important 通常是「特异性打架打输了」的求救信号,看到就知道这块样式可能很脆。
2. 来源与顺序 特异性相同时,后写的覆盖先写的。这就是为什么很多组件库要求你的样式表在它后面引入。
3. 继承(Inheritance) color、font-*、line-height 这些会往子元素传;margin、padding、border、background 不会。看到子元素莫名其妙有了某个颜色,多半是继承下来的。
一个常见现代写法:
all: unset或all: revert,意思是「把这个元素的样式全部清空/还原」,常用在重置按钮、input 的默认外观上。
二、布局只剩两套:Flexbox 和 Grid
现在的代码里,几乎所有布局非 Flex 即 Grid,老的 float 布局基本只在很旧的项目里见到。
Flexbox —— 一维(一行或一列)
.bar {
display: flex;
justify-content: space-between; /* 主轴:左右怎么分布 */
align-items: center; /* 交叉轴:垂直怎么对齐 */
gap: 12px; /* 间距,取代了到处写 margin */
}
看到 display: flex 就想「一排东西,要么横排要么竖排」。justify-* 管主轴,align-* 管交叉轴,记住这一对就够读懂九成 Flex 代码。
Grid —— 二维(行 + 列)
.layout {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 三等分列,1fr = 一份剩余空间 */
gap: 16px;
}
/* 自适应卡片墙的经典写法,看到 auto-fill/minmax 基本就是它 */
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
fr 是 grid 专属单位,「剩余空间的份数」。minmax(200px, 1fr) 读作「每列最小 200px,能撑大就撑大」——这是响应式卡片墙最常见的一行,认得它能省很多事。
三、容器查询(Container Queries):组件响应式的新标准
这是近几年最值得专门记的一个。传统 @media 看的是整个视口宽度;容器查询看的是元素所在容器的宽度。它在 2025 年正式进入 Baseline「广泛可用」,新项目里越来越多。
.card-wrapper {
container-type: inline-size; /* 声明:我是一个可被查询的容器 */
}
@container (min-width: 400px) {
.card { flex-direction: row; } /* 容器够宽时卡片横排 */
}
为什么重要:同一个卡片组件,放侧边栏里是窄的、放主区域是宽的,用容器查询它能自己适配,不用关心整页多宽。看到 container-type 和 @container,就知道作者在写「自适应组件」而不是「自适应页面」。
四、自定义属性(CSS Variables):现代代码的水电煤
几乎所有有点规模的项目、所有设计系统都靠它。读代码时遇到大量 var(--xxx) 是常态。
:root {
--brand: #4f46e5;
--space: 8px;
}
.btn {
background: var(--brand);
padding: calc(var(--space) * 2); /* 变量能参与计算 */
color: var(--btn-fg, white); /* 第二个参数是兜底默认值 */
}
几个关键认知:
- 它是会继承、能在运行时被 JS 改的(不像 Sass 变量是编译期写死的)。看到 JS 里
element.style.setProperty('--x', ...),就是在动态改主题/动画。 - 命名约定里
--_x(带下划线)通常表示「组件内部私有变量」。 - 换肤、暗色模式、主题色,背后基本都是一套 CSS 变量在切。
五、现代选择器::has()、:is()、:where()、嵌套
:has() —— 父选择器,CSS 几十年来最大的补强之一。终于能「根据子元素的状态来选父元素」:
/* 选中「包含被勾选 checkbox 的」表单项 */
.field:has(input:checked) { background: #eef; }
/* 卡片里有图片时给不同的内边距 */
.card:has(img) { padding-top: 0; }
看到 :has() 就明白:作者在用纯 CSS 做以前必须靠 JS 判断的逻辑。
:is() 和 :where() —— 把一堆选择器合并写:
/* 等价于 h1 a, h2 a, h3 a */
:is(h1, h2, h3) a { color: inherit; }
两者唯一区别::where() 的特异性永远是 0(不会和别人抢覆盖优先级),所以重置样式、组件库基础样式特别爱用它,方便你后面轻松覆盖。
原生嵌套(Nesting) —— 不用 Sass 也能嵌套了,现在直接出现在普通 .css 文件里:
.card {
color: #333;
&:hover { color: #000; } /* & 指代父选择器 */
.title { font-weight: 700; }
}
看到 & 和层层缩进的原生 .css,不要以为是 Sass,可能就是浏览器原生嵌套。
六、@layer 层叠层 与 @scope:管「谁覆盖谁」
@layer(Cascade Layers) 是读大型项目时容易懵的一个。它显式规定了不同样式层之间的优先级,层的顺序优先于特异性。
@layer reset, base, components, utilities; /* 越靠后优先级越高 */
@layer components {
.btn { color: blue; }
}
@layer utilities {
.text-red { color: red; } /* 即使特异性更低,也能覆盖上面 */
}
为什么要知道它:在用了 @layer 的项目里,「特异性更高却没生效」是正常现象——因为层的顺序压过了特异性。不了解这点会 debug 到怀疑人生。
@scope 则是把样式限制在某个 DOM 范围内,避免样式泄漏到外面:
@scope (.card) to (.card-footer) {
a { color: var(--brand); } /* 只作用于 .card 内、.card-footer 外 */
}
七、现代单位与函数:让样式自己算
这些在新代码里密度很高,认得就能秒懂作者意图。
clamp() / min() / max() —— 流式响应
/* 字号在 16px~24px 之间随视口平滑缩放,几乎取代了一堆 @media 断点 */
font-size: clamp(16px, 2.5vw, 24px);
clamp(最小, 理想, 最大),看到它就是「这个值会自适应,但有上下限」。
视口单位 dvh / svh / lvh —— 解决移动端「100vh 被地址栏挡住」的老问题。dvh 是动态视口高度,移动端全屏布局现在基本都用它。
aspect-ratio —— 锁定宽高比,取代了以前的 padding-hack:
.video { aspect-ratio: 16 / 9; }
逻辑属性(Logical Properties) —— 跟书写方向走,做多语言/国际化项目必见:
margin-inline: auto; /* 水平方向(= 左右)居中 */
padding-block: 16px; /* 垂直方向(= 上下) */
inset: 0; /* top/right/bottom/left 全 0,常用于定位铺满 */
看到 inline(行内/水平方向)和 block(块/垂直方向)这套词,就是逻辑属性。新单位 lh(一个行高的高度)也开始进入主流,方便让徽标、高亮块精确等于一行文字的高度。
八、颜色现代化:oklch、color-mix、light-dark
颜色这块这两年变化挺大,新设计系统几乎都在用。
oklch() —— 比 HSL 更符合人眼感知的色彩空间。最大好处:调亮度/色相时不会出现 HSL 那种「不同色相看起来亮度不一致」的问题,做色板特别好用。
--brand: oklch(60% 0.15 264); /* 亮度 色度 色相 */
color-mix() —— 在 CSS 里直接混色,省掉一堆预先定义的浅色/深色变体:
background: color-mix(in oklch, var(--brand) 20%, white); /* 品牌色的浅色版 */
light-dark() —— 一行搞定明暗双色,配合 color-scheme 用:
:root { color-scheme: light dark; }
body { background: light-dark(white, #111); }
看到这些,基本能判断这是个比较新、比较讲究的代码库。
九、值得「认识」的新东西(2025–2026)
下面这些大多还在逐步铺开(有的尚未完全 Baseline),但已经开始出现在新代码和文章里。读代码时认得出、知道大概干嘛的就够了,不必现在就背用法。
- 锚点定位(Anchor Positioning):用
anchor-name/position-anchor/position-area,让一个元素「拴」在另一个元素旁边。工具提示、下拉菜单、浮层以前要靠 JS 算坐标,现在 CSS 原生能做。 - Popover API:HTML 的
popover属性 + 按钮上的popovertarget,纯声明式做弹层/提示,不写 JS。 - 视图过渡(View Transitions):
view-transition-name+ JS 的startViewTransition(),给页面/状态切换加上丝滑过渡动画,单页内过渡已进入 Baseline。 - 滚动驱动动画(Scroll-driven Animations):用
animation-timeline: scroll()/view()把动画进度绑到滚动位置,进度条、视差等不再需要监听 scroll 事件。 - 可定制的
<select>:通过appearance: base-select,原生下拉框终于能自由换样式,同时保留无障碍能力。 @starting-style:给元素「进场」时指定起始样式,让刚出现的元素也能有过渡动画(配合 popover、display: none切换很常见)。sibling-index()/sibling-count():让元素知道自己排第几,纯 CSS 做交错(stagger)动画,例如transition-delay: calc((sibling-index() - 1) * 100ms)。- 类型化
attr():attr(data-columns number, 3),能把 HTML 的data-*属性当带类型的值用在任意属性上,搭起 HTML↔CSS 的桥。 text-box-trim/text-box:裁掉字体自带的上下空隙,做到像素级垂直居中,排版党的福音。text-wrap: balance/pretty:自动让标题/段落换行更好看(标题各行更均衡,段落避免末行只剩一个字)。field-sizing: content:让textarea、input随内容自动撑高/撑宽,告别 JS 测量高度。
给「读代码者」的几条实用提醒
- 遇到不认识的属性,先查它的 Baseline 状态。MDN 和 caniuse 现在都直接标注「Widely available / Newly available / Limited」。看到 Limited 的,多半是渐进增强或实验代码,不是主流必备。
!important和满屏#id选择器通常是技术债信号,看到这类代码对「为什么难改」要有心理预期。- 现代项目越来越「少 JS 化」:很多以前必须 JS 的交互(弹层、下拉、滚动动画、交错动画),现在 CSS 原生能做。看到
:has()、popover、anchor-*、animation-timeline,就知道作者在走这条路线。 - 想持续跟进,每年看一篇就够:Chrome 团队的 CSS Wrapped、State of CSS 年度调查、web.dev 的 Baseline 月报,都是按「今年有什么新东西、能不能用了」组织的,对读代码非常对口。
简单说,2026 年读 CSS 代码的核心能力 = 把住「层叠规则」这条命脉 + 认得 Flex/Grid/容器查询/变量/现代选择器这套主力 + 对一堆 2025–2026 的新特性「混个脸熟」。