← 返回文章列表
CSS·128 次阅读

现代 CSS 三件套::has()、容器查询与原生嵌套

现代 CSS 三件套::has()、容器查询与原生嵌套

这几年 CSS 的进化速度明显加快,2023 年前后落地的几个特性直接改变了很多"必须靠 JS"或"必须靠预处理器"的写法。主流浏览器现在都已支持,可以放心在生产里用了。

:has():等了二十年的父选择器

以前 CSS 只能从父选到子,想根据"内部状态"改变容器样式必须靠 JS 加 class。:has() 终结了这个历史:

/* 表单里某个输入框校验失败时,整个表单项标红 */
.form-item:has(input:invalid) {
  border-color: red;
}

/* 卡片里有图片时才给图片留白 */
.card:has(img) {
  padding-bottom: 0;
}

/* 子元素超过 5 个就换紧凑布局 */
.grid:has(> :nth-child(5)) {
  gap: 8px;
}

/* 纯 CSS 实现"聚焦某个输入时高亮所在行" */
tr:has(:focus-within) {
  background: #fef3c7;
}

注意 :has() 不进权重计算的误区——它本身和 :not() 一样,权重取参数里最高的那个选择器,所以 :has(#id) 的权重会很高,小心覆盖问题。

性能方面早期有顾虑,实际主流引擎的实现已经很好,日常规模的 DOM 随便用。

容器查询:组件级的响应式

媒体查询只能响应视口宽度,但组件常常被放在不同宽度的位置——同一卡片在侧边栏和主栏里应该长得不一样。容器查询让组件响应自身容器的宽度:

/* 1. 声明一个容器 */
.sidebar,
.main {
  container-type: inline-size;
  container-name: panel; /* 可选命名 */
}

/* 2. 查询容器宽度而不是视口 */
@container panel (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 120px 1fr;
  }
}

这样 .card 放到窄的侧边栏自动变纵向、放到宽的主栏自动变横向,组件真正做到了"自带响应式",不用再给使用方传 layout 参数。

配套的容器查询单位也很好用:cqw(容器宽度的 1%)、cqi 等,做"字号随容器缩放"特别方便。

一个坑:container-type: inline-size 会让容器的高度塌缩为内容高度(因为它不能再从子元素推导尺寸),布局上要留意。

原生嵌套:CSS 预处理器最后的护城河没了

.card {
  padding: 16px;

  .title {
    font-size: 20px;

    &:hover {
      color: #059669;
    }
  }

  @media (min-width: 768px) {
    padding: 24px;
  }
}

原生嵌套的语法和 Sass 几乎一致,& 引用父选择器,媒体查询也能直接写在规则里。和 Sass 的一个区别:嵌套的标签选择器必须加 & 或用 :is(),不能裸写 div { },这是语法解析上的限制。

原生嵌套落地后,新项目真的需要装 Sass 的场景越来越少——变量有 CSS custom properties,嵌套原生支持,循环之类的用 PostCSS 或干脆构建时生成。

什么时候可以放心用

特性 Chrome Safari Firefox 我的判断
:has() 105+ 15.4+ 121+ 可生产,Firefox 2023 底补齐
容器查询 105+ 16+ 110+ 可生产
原生嵌套 120+ 17.2+ 117+ 新项目直接用,老项目渐进

写 CSS 的乐趣这几年明显回来了。以前很多"CSS 做不到"的清单正在被一条条划掉,值得把脑子里的旧认知刷新一遍。

看到这里了?如果这篇文章帮到过你

请我喝瓶可乐就好 🥤

收款码

评论0 条

还没有评论,来抢沙发~

发表评论

评论需审核后显示

© 2026 许冬博客 · 专注 JS 技术分享

·RSS 订阅·Powered by Nuxt 4