CSS容器查询(Container Queries)改变了响应式布局的实现方式。传统媒体查询基于视口宽度判断断点,无法适配组件在不同容器尺寸下的响应式需求。容器查询允许组件根据其父容器宽度独立响应,实现真正的组件级响应式设计。Container Queries在2023年获得全浏览器支持后,已成为现代CSS响应式布局的标准方案,配合container-type、@container等新特性,可以构建完全解耦的可复用组件。
容器查询基础概念与容器类型声明
容器查询的核心是将响应式判断的基准从视口转移到组件容器。使用container-type属性声明容器,组件内部即可通过@container规则根据容器尺寸应用不同样式。container-type:inline-size基于容器内联尺寸(通常是宽度)进行查询,适用于大多数响应式布局场景。container-type:size同时基于宽度和高度查询,但会创建新的包含上下文,影响子元素的尺寸计算。
/* 声明容器上下文 */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* 简写形式 */
.sidebar {
container: sidebar / inline-size;
}
/* 基于容器尺寸的条件样式 */
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
.card-image {
width: 200px;
flex-shrink: 0;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
.card-image {
width: 100%;
height: 200px;
}
}
/* 容器尺寸区间查询 */
@container card (min-width: 300px) and (max-width: 600px) {
.card-title {
font-size: 1.25rem;
}
}
容器查询与媒体查询协同布局方案
容器查询不替代媒体查询,两者协同使用构建多层级响应式体系。媒体查询处理页面级布局(如侧边栏显示/隐藏、网格列数变化),容器查询处理组件级布局(如卡片排列方向、导航栏样式)。这种分层策略使组件可以在不同页面位置自适应渲染,无需感知外层布局变化。
/* 页面级布局:媒体查询控制整体结构 */
.page-layout {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 768px) {
.page-layout {
grid-template-columns: 240px 1fr;
}
}
@media (min-width: 1200px) {
.page-layout {
grid-template-columns: 240px 1fr 300px;
}
}
/* 组件级布局:容器查询控制组件内部样式 */
.widget {
container-type: inline-size;
container-name: widget;
}
@container widget (min-width: 500px) {
.widget-header {
flex-direction: row;
justify-content: space-between;
}
.widget-actions {
position: static;
}
}
@container widget (max-width: 499px) {
.widget-header {
flex-direction: column;
gap: 0.5rem;
}
.widget-actions {
position: sticky;
bottom: 0;
}
}
/* 嵌套容器查询 */
.dashboard-grid {
container-type: inline-size;
container-name: dashboard;
}
@container dashboard (min-width: 800px) {
.dashboard-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.dashboard-grid .widget {
container-type: inline-size;
container-name: widget;
}
@container widget (min-width: 300px) {
.widget-body {
padding: 1.5rem;
}
}
}
容器查询单位cqw与流体排版
容器查询单位(cqw、cqh、cqi、cqb、cqmin、cqmax)基于容器尺寸计算,类似于视口单位(vw、vh)但基准是容器而非视口。使用cqw单位实现流体排版,字体大小随容器宽度平滑缩放,无需定义多个断点。
/* cqw: 容器宽度的1% */
/* cqi: 容器内联尺寸的1%(等同于cqw) */
/* cqh: 容器高度的1% */
/* cqb: 容器块尺寸的1% */
/* cqmin: cqi和cqb中较小的值 */
/* cqmax: cqi和cqb中较大的值 */
.article-card {
container-type: inline-size;
container-name: article;
}
/* 流体排版:标题大小随容器宽度缩放 */
@container article (min-width: 0) {
.article-title {
font-size: clamp(1rem, 5cqw, 2.5rem);
line-height: clamp(1.2, 2cqw, 1.6);
}
.article-meta {
font-size: clamp(0.75rem, 2.5cqw, 1rem);
}
.article-excerpt {
font-size: clamp(0.875rem, 3cqw, 1.125rem);
max-width: 65ch;
}
}
/* 流体间距 */
@container article (min-width: 0) {
.article-content {
padding: clamp(0.75rem, 4cqw, 2rem);
gap: clamp(0.5rem, 2cqw, 1.5rem);
}
}
/* 流体网格列数 */
@container article (min-width: 0) {
.article-tags {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(clamp(80px, 20cqw, 150px), 1fr));
gap: clamp(0.25rem, 1cqw, 0.75rem);
}
}
组件库中的容器查询实践
在组件库设计中,容器查询让组件实现真正的自适应。同一组件放在侧边栏宽容器或主内容区窄容器中,自动调整内部布局,无需外部传入size prop或使用媒体查询。
<!-- HTML结构 -->
<div class="product-card-wrapper">
<article class="product-card">
<div class="product-card__media">
<img src="product.jpg" alt="产品图" />
<span class="product-card__badge">新品</span>
</div>
<div class="product-card__body">
<h3 class="product-card__title">产品名称</h3>
<p class="product-card__desc">产品描述文本</p>
<div class="product-card__meta">
<span class="product-card__price">¥299</span>
<span class="product-card__rating">★4.5</span>
</div>
<div class="product-card__actions">
<button class="btn btn--primary">加入购物车</button>
<button class="btn btn--ghost">收藏</button>
</div>
</div>
</article>
</div>
/* 容器查询驱动的卡片组件 */
.product-card-wrapper {
container-type: inline-size;
container-name: card;
}
.product-card {
border-radius: 12px;
overflow: hidden;
background: #fff;
border: 1px solid #e2e8f0;
transition: box-shadow 0.2s;
}
.product-card:hover {
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}
/* 默认:窄容器垂直布局 */
@container card (max-width: 299px) {
.product-card {
display: flex;
flex-direction: column;
}
.product-card__media {
aspect-ratio: 16 / 9;
}
.product-card__media img {
width: 100%;
height: 100%;
object-fit: cover;
}
.product-card__body {
padding: 0.75rem;
}
.product-card__title {
font-size: clamp(0.875rem, 4cqw, 1rem);
}
.product-card__desc {
font-size: 0.75rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-card__actions {
flex-direction: column;
}
}
/* 中等容器:紧凑水平布局 */
@container card (min-width: 300px) and (max-width: 499px) {
.product-card {
display: flex;
flex-direction: row;
}
.product-card__media {
width: 40%;
flex-shrink: 0;
}
.product-card__body {
padding: 1rem;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.product-card__actions {
flex-direction: row;
gap: 0.5rem;
}
}
/* 宽容器:卡片网格布局 */
@container card (min-width: 500px) {
.product-card {
display: flex;
flex-direction: column;
}
.product-card__media {
aspect-ratio: 4 / 3;
}
.product-card__body {
padding: clamp(1rem, 3cqw, 2rem);
}
.product-card__title {
font-size: clamp(1.125rem, 4cqw, 1.5rem);
}
.product-card__desc {
font-size: clamp(0.875rem, 2cqw, 1rem);
-webkit-line-clamp: 3;
}
.product-card__meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-card__actions {
flex-direction: row;
gap: 0.75rem;
}
}
容器查询与CSS自定义属性联动
容器查询可以与CSS自定义属性(CSS Variables)联动,通过@container规则动态修改自定义属性值,实现更灵活的响应式样式控制。
:root {
--layout-mode: compact;
--columns: 1;
--gap: 0.5rem;
}
.grid-container {
container-type: inline-size;
container-name: grid;
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--gap);
}
@container grid (min-width: 600px) {
.grid-container {
--layout-mode: comfortable;
--columns: 2;
--gap: 1rem;
}
}
@container grid (min-width: 900px) {
.grid-container {
--layout-mode: spacious;
--columns: 3;
--gap: 1.5rem;
}
}
/* JavaScript感知容器查询状态 */
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const cr = entry.contentRect;
const container = entry.target;
const layoutMode = getComputedStyle(container)
.getPropertyValue('--layout-mode').trim();
container.dispatchEvent(new CustomEvent('layout-change', {
detail: { mode: layoutMode, width: cr.width }
}));
}
});
document.querySelectorAll('.grid-container').forEach(el => {
observer.observe(el);
el.addEventListener('layout-change', (e) => {
console.log(`Layout mode: ${e.detail.mode}, width: ${e.detail.width}px`);
});
});
容器查询兼容性处理与渐进增强
虽然主流浏览器已支持容器查询,但在需要兼容旧浏览器的项目中仍需做降级处理。使用@supports规则检测容器查询支持情况,不支持时回退到媒体查询方案。
/* 基础样式:不依赖容器查询的降级方案 */
.product-card {
display: flex;
flex-direction: column;
}
/* 支持容器查询时的增强方案 */
@supports (container-type: inline-size) {
.product-card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 500px) {
.product-card {
display: flex;
flex-direction: row;
}
}
}
/* 不支持容器查询时的媒体查询降级 */
@supports not (container-type: inline-size) {
@media (min-width: 768px) {
.product-card {
display: flex;
flex-direction: row;
}
}
}
容器查询将响应式设计的控制粒度从页面级细化到组件级,解决了组件复用中的布局适配难题。在实际项目中,容器查询配合CSS Grid和Flexbox可以构建完全自适应的组件系统。使用cqw单位实现流体排版时注意设置clamp上下限,避免极端容器尺寸下字体过大或过小。容器查询不支持嵌套同名容器,嵌套容器需要使用container-name区分。性能方面,容器查询的重新计算成本高于媒体查询,避免在深层嵌套容器中大量使用复杂的容器查询条件。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/css-rong-qi-cha-xun-containerqueries-yu-xiang-ying-shi-bu/