问题场景
样式调了半天,为什么这个 position: relative 还是被别的地方覆盖?为什么相邻的元素 margin 会"塌陷"?为什么用 !important 越改越乱?Flex 和 Grid 到底该用哪个?
CSS 的层叠(cascade)与格式化上下文(BFC)+ 现代布局体系(Flex/Grid)+ 层优先级(@layer) ,是高级前端绕不开的三件套。理解它们,你再也不用靠 !important 碰运气,也不用在每个布局上反复试错。
原理深入
1. 层叠(Cascade):到底谁赢?
当一个元素命中多条 CSS 规则,靠"层叠"判定优先级:
text
!important > 内联样式 > 选择器特异性 > 源码顺序
选择器特异性(specificity) :(内联, id, 类/属性/伪类, 元素/伪元素)
css
a /* (0,0,0,1) */
.class /* (0,0,1,0) */
#id /* (0,1,0,0) */
#id .class /* (0,1,1,0) */
逐项比较:从左到右,第一个更大的赢;相等才看源码顺序(后面的赢)。
css
.box { color: red; } /* (0,0,1,0) */
div.box { color: blue; } /* (0,0,1,1) → 赢,因为多了元素 */
!important是"绕过规则",导致层叠失控------能不用就不用,治理靠 @layer。
2. BFC / IFC:块级格式化上下文
BFC(Block Formatting Context) 是一个"隔离的布局容器",内部规则不影响外部。触发 BFC:overflow:hidden、float、position:absolute/fixed、display:inline-block、display:flex/grid 等。
BFC 解决三个经典问题:
css
/* ① 清除浮动(父容器塌陷) */
.parent { overflow: hidden; } /* 触发 BFC,包含浮动子元素 */
/* ② 阻止 margin 塌陷 */
/* 两个相邻元素 margin-bottom + margin-top 会合并为较大值 */
/* 解决:给其中一个加 BFC 隔离 */
.one { margin-bottom: 30px; }
.two { margin-top: 20px; overflow: hidden; } /* 实际间距是 30 而不是合并后的重叠 */
/* ③ 阻止元素被浮动元素覆盖 */
.right { float: left; }
.left { overflow: hidden; } /* BFC 不与 float 重叠,形成两栏 */
3. Flex vs Grid:怎么选?
Flex(一维):主轴 + 交叉轴,适合"一行/一列的分布、对齐、伸缩"。
css
.container { display: flex; }
.item { flex: 1; } /* 等分布局 */
Grid(二维):行列网格,适合"整体页面/卡片网格布局"。
css
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* 自适应列 */
gap: 16px;
}
取舍原则:
- 一维(导航栏、按钮组、单行分布)→ Flex
- 二维(卡片网格、复杂页面骨架)→ Grid
- 经典组合:外层 Grid 定页面骨架,区域内 Flex 管一维分布。
4. @layer:治理层叠的现代方案(2022+ 全支持)
@layer 让你显式声明层的优先级,与选择器特异性解耦:
css
@layer reset, base, components, utilities; /* 靠后声明的层优先级更高 */
@layer reset { .button { color: blue; } }
@layer components { .button { color: red; } }
/* .button = red(components 层在 reset 之后)即使 reset 特异性更高 */
- 未分层样式 > 任何 @layer(兜底最高)。
- 用它把第三方库压进低优先层:
@import url(x.css) layer(vendor),从此覆盖库样式不再靠!important死磕。 - 注意:层内
!important的优先级反转 (靠前的层更重),避免在层内用!important。
5. 现代布局新能力(2023-2024)
css
/* container query:按容器宽度响应(组件级响应式) */
.card { container-type: inline-size; }
@container (min-width: 400px) { .card { display: flex; } }
/* :has():父选择器 */
.input-wrap:has(input:focus) { border-color: blue; }
/* subgrid:子网格沿用父网格轨道 */
.inner { grid-template-columns: subgrid; }
/* aspect-ratio、dvh 视口单位 */
.video { aspect-ratio: 16/9; }
.hero { height: 100dvh; }
实战代码
用 @layer + Grid 搭一套"干净的样式治理 + 响应式卡片":
css
/* 层优先级声明 */
@layer reset, theme, components;
@layer reset {
* { margin: 0; box-sizing: border-box; }
}
@layer components {
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
.card { border: 1px solid #ddd; padding: 16px; }
.card__title { font-weight: 700; }
}
html
<div class="card-grid">
<div class="card"><span class="card__title">卡片1</span></div>
<div class="card"><span class="card__title">卡片2</span></div>
</div>
效果:grid 自适应列自适应小屏,reset 最低优先级不干扰,组件层清晰。
要点总结
- 层叠 :
!important > 内联 > 特异性 > 顺序;特异性逐位比较。 - BFC :隔离的布局上下文,能清浮动、防 margin 塌陷、文档流分栏;
overflow:hidden等可触发。 - Flex 一维配分布,Grid 二维配骨架,外层 Grid + 内层 Flex 是黄金组合。
- @layer 显式分层优先级,治理第三方库和团队样式,不再依赖
!important。 - 现代布局:
minmax、auto-fill、gap、aspect-ratio让响应式更省心。 - 排查思路:样式被盖 → 先算特异性;间距不对 → 查 margin 塌陷/BFC;覆盖库样式 → 用 @layer。
一句话:层叠管"谁赢",BFC 管"隔离",Flex/Grid 管"怎么排",@layer 管"治理优先级"。四件套齐全,CSS 从"调参赌运气"变成"可控的工程系统"。