POST://css-container-queries-has
带点橘子味的馒头的头像
带点橘子味的馒头

FRONTEND / DESKTOP DEV

返回文章列表

别再写死断点了:容器查询与 :has() 带来的布局革命

1 分钟· 837 1 次阅读

做组件库最尴尬的事:同一个卡片,放在侧边栏很挤、放在主区很空。过去只能靠媒体查询硬切,但媒体查询盯的是视口,不是组件真正的大小。好消息是,现在可以用容器查询:has() 解决。

媒体查询的死穴

/* 旧思路:根据整个屏幕宽度决定卡片布局 */
@media (min-width: 1024px) {
  .card { display: flex; }
}

问题在于:一张卡片在 1024px 的页面里可能位于 300px 的侧栏,也可能位于 700px 的主区。视口相同,可用空间却完全不同——媒体查询无能为力。

容器查询:组件按"自己的容器"响应

先声明"我是可被查询的容器",再基于容器宽度写样式:

.card-wrapper {
  container-type: inline-size; /* 声明容器,可按宽度查询 */
}

@container (min-width: 480px) {
  .card {
    display: flex;
    gap: 16px;
  }
  .card__thumb {
    width: 120px;
    flex-shrink: 0;
  }
}

现在卡片的行为只取决于它实际被放进多宽的容器——放在窄侧栏就纵向堆叠,放进宽主区就横向排布。组件从此"到哪都能用",不再依赖页面布局。

维度 媒体查询 @media 容器查询 @container
响应对象 视口 / 设备 最近的容器元素
复用性 低(绑定页面) 高(组件自包含)
典型用途 整页断点 卡片、列表、组件内部

:has():CSS 第一次能"向上选"

过去 CSS 只能选后代、兄弟,没法根据"有没有某个子元素"来给父级加样式:has() 补上了这个缺口:

/* 只要组内包含图片,就切换成图文布局 */
.media-group:has(img) {
  display: grid;
  grid-template-columns: auto 1fr;
}

/* 表单里只要有一个必填项没填,给整张卡片描红边 */
.form-card:has(input:invalid) {
  border-color: #e5484d;
}

:has() 本质是"父级根据子级状态做反应",这在以前必须靠 JS 监听才能做到。

两者合体:自适应 + 自感知

容器查询解决"空间够不够",:has() 解决"内容是什么",组合起来组件就真正智能了:

.product {
  container-type: inline-size;
}

/* 空间够宽,且带评分徽章时,才展示横向图文布局 */
.product:has(.badge) {
  @container (min-width: 420px) {
    display: flex;
    align-items: center;
  }
}

这样一个商品卡:窄容器里自动堆叠;宽容器里自动横排;一旦带促销徽章,还会额外切换成强调样式——全程零 JS

兼容性怎么看

  • 容器查询:现代浏览器(Chrome/Edge/Firefox/Safari 近两年版本)均已支持,生产可用;
  • :has():同样主流已支持,连 Safari 都早早跟进。

老浏览器兜底很简单:把新写法当成"渐进增强",默认给一个基础布局,支持的新浏览器再叠加精致布局。即使不支持,页面也只是"没那么自适应",不会崩。

小结

两个特性把"响应式"的重心从页面挪回了组件

  1. 容器查询让组件按自身容器宽度响应,彻底摆脱视口束缚;
  2. :has() 让父级能根据子级是否存在 / 是否某状态来变化,补足了 CSS 最后一块短板;
  3. 合起来,很多原本要写 JS 才能做的"智能布局",现在纯 CSS 就能搞定,且性能更好。

做组件库或设计系统时,这俩值得第一时间用上。

相关文章