媒体查询一直很好用,但它回答的是“浏览器窗口有多宽”。当同一个商品卡片既会出现在三栏列表,也会出现在侧边推荐区时,窗口宽度已经不能说明卡片有多少空间。以前我会加一个 compact 属性,让调用方告诉组件怎么排;现在容器查询让组件自己判断。
同一屏里的两个卡片,需要两种布局
桌面端主列表里的卡片大约四百像素宽,图片和说明适合左右排列;侧栏只有两百多像素,只能上下排。如果写 @media (min-width: 900px),两边都会命中,因为它们共享同一个视口。于是样式不得不依赖页面结构,组件换个位置就容易坏。
容器查询把判断标准换成最近的查询容器。调用方只负责给空间,组件根据空间选择布局,不需要知道自己在“首页”还是“详情页”。
两段 CSS 就能开始
.card-slot {
container-type: inline-size;
}
.product-card {
display: grid;
gap: 1rem;
}
@container (min-width: 360px) {
.product-card {
grid-template-columns: 120px 1fr;
}
}
container-type: inline-size 表示只关注行内方向的尺寸,在普通横排文字里基本就是宽度。如果页面上有多层容器,可以再加 container-name,让查询明确指向某一层,避免以后结构调整时命中错误的父元素。
断点仍然来自内容:当标题和按钮开始拥挤时记录那个宽度,而不是沿用“平板 768px”这样的设备数字。
实践里遇到的三个小问题
第一,查询容器通常应该放在组件外层。如果直接把组件本身设成容器,它不能查询自己的尺寸来修改自己,但它的子元素可以。第二,容器建立尺寸隔离后,某些依赖内容撑开的布局会变化,上线前要检查高度和溢出。
第三,不必把所有媒体查询都替换掉。页面整体导航、视口高度和用户输入方式仍然适合媒体查询;容器查询更适合卡片、工具栏、表单组这些会被放进不同位置的模块。
这次改完以后,卡片少了一个展示属性,也不再需要侧栏专用 class。代码量没有少很多,但组件对外部环境的假设更少了,这种“搬到哪里都能正常工作”就是容器查询最实在的价值。