媒体查询为什么不够用了

过去做响应式,全靠 @media:判断视口宽度,然后改样式。问题是组件的形态其实不取决于视口,而取决于它被放在多大的容器里。同一个卡片组件,放在宽栏是横排、塞进窄侧栏就得竖排,但它根本不知道自己现在占多宽——媒体查询只能看到整个屏幕。

结果就是同一套组件要为不同位置写一堆重复的断点,组件失去了“自适应性”。现代 CSS 的几个新特性,正是冲着这个痛点来的:容器查询让组件按容器尺寸适配、子网格让嵌套布局对齐、级联层让样式优先级变得可管理。

本文核心知识点
  • 容器查询组件按父容器宽度变化,不再盯视口。
  • 子网格子元素能直接对齐父网格的轨道。
  • 级联层用 @layer 显式分层控制样式优先级。
  • 渐进增强新特性按支持度优雅降级,不拖累老浏览器。
现代 CSS 配图:蓝色渐变背景上嵌套的圆角容器方块层层套叠,内部有整齐网格对齐线,旁边有层叠色块堆栈,扁平科技插画
嵌套容器、内部网格对齐线与层叠色块,对应容器查询、子网格与级联层。

容器查询:按容器宽度适配

容器查询分两步:先给某个父元素声明它是“查询容器”,再对子元素写“当容器多宽时怎么排版”。这样组件搬来搬去,会根据当前所在容器的宽度自动切换布局,真正做到组件自包含。

/* 把卡片列表声明为查询容器 */
.card-list { container-type: inline-size; }

/* 卡片在容器够宽时横排,窄时竖排 */
@container (min-width: 480px) {
  .card { display: flex; gap: 1rem; }
}

注意 container-type: inline-size 是按内联方向(通常是宽度)查询。配合 @container,以前要写在媒体查询里的那套横排竖排逻辑,现在下沉到组件内部,侧栏、主区、弹窗里放同一个组件都能自适应。

子网格:对齐父子网格

普通网格里,子元素只能在自己这一层的网格里排布;如果子元素内部又套了一层网格,两层网格的轨道对不齐,卡片之间的标题、图片、文字就会参差不齐。subgrid 让子元素直接借用父网格的轨道。

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}
/* 子元素复用父网格的列轨道,保证全站对齐 */
.grid > .cell {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
}

有了 subgrid,嵌套网格的行和列能和外层对齐,多卡片布局高度一致、视觉整齐,不再要靠固定高度或魔法数字去硬凑。

级联层:把优先级交给你自己

CSS 历来最大的痛是优先级打架:选择器越写越长、!important 满天飞,最后谁也说不清哪个样式会生效。级联层(@layer)让你显式给样式分“层”,层与层的优先级由你声明的顺序决定,而不是被选择器权重绑架。

@layer reset, base, components, utilities;

@layer components {
  .btn { padding: .5rem 1rem; }
}
@layer utilities {
  /* utilities 层最后声明,天然压过 components */
  .mt-2 { margin-top: .5rem; }
}

越靠后声明的层优先级越高。配合设计系统,把 reset、基础、组件、工具类分成四层,工具类放心写、组件类不被工具类意外覆盖,再也不用靠 !important 赢。

逻辑属性与 :has 选择器

另外两个值得知道的现代特性:逻辑属性用 margin-inline-start、padding-block 这类按书写方向的属性,替代 left/right 硬编码,页面一旦换方向(如阿拉伯语从右到左)自动适配;:has() 选择器能根据父元素是否包含某个子元素来给父元素加样式,比如“当卡片里有图时给它特殊内边距”,过去要靠 JS 判断,现在一行 CSS 搞定。

/* 卡片内部有图时,取消卡片自身的上间距 */
.card:has(img) { padding-top: 0; }

/* 用逻辑属性替代硬编码左右 */
.box { margin-inline-start: 1rem; }