Skip to content

容器查询

容器查询(Container Queries)是 CSS 最受期待的特性之一。它让组件可以根据父容器的尺寸来调整样式,而不是依赖视口宽度。这对构建真正可复用的响应式组件至关重要。

为什么需要容器查询#

传统媒体查询的问题:

/* 媒体查询基于视口宽度 */
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}

同一个卡片组件可能出现在不同宽度的容器中:

但媒体查询无法区分这些场景——它只知道视口宽度。

容器查询解决了这个问题:

/* 基于容器宽度 */
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}

基本用法#

定义容器#

使用 container-type 将元素标记为容器:

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

container-type 值:

编写容器查询#

@container (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
.card-image {
width: 150px;
flex-shrink: 0;
}
}

完整示例#

.card-wrapper {
container-type: inline-size;
}
.card {
padding: 1rem;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 8px;
}
.card-content {
margin-top: 1rem;
}
/* 容器宽度 >= 400px 时水平布局 */
@container (min-width: 400px) {
.card {
display: flex;
gap: 1.5rem;
}
.card-image {
width: 200px;
aspect-ratio: 1;
}
.card-content {
margin-top: 0;
flex: 1;
}
}
/* 容器宽度 >= 600px 时更大的图片 */
@container (min-width: 600px) {
.card-image {
width: 300px;
}
}

命名容器#

当有多个嵌套容器时,使用名称区分:

.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.main {
container-type: inline-size;
container-name: main;
}
/* 简写 */
.sidebar {
container: sidebar / inline-size;
}
/* 查询特定容器 */
@container sidebar (min-width: 300px) {
.widget {
padding: 1.5rem;
}
}
@container main (min-width: 800px) {
.article {
columns: 2;
}
}

容器查询单位#

容器查询引入了新的相对单位:

单位描述
cqw容器宽度的 1%
cqh容器高度的 1%
cqi容器内联尺寸的 1%
cqb容器块尺寸的 1%
cqmincqicqb 中较小者
cqmaxcqicqb 中较大者
.container {
container-type: inline-size;
}
.title {
font-size: clamp(1rem, 5cqi, 2rem);
}
.hero {
height: 50cqh;
}

样式查询(实验性)#

除了尺寸,还可以查询容器的样式值:

.card {
--theme: light;
}
@container style(--theme: dark) {
.card-content {
color: white;
}
}

🔶 样式查询目前仅部分浏览器支持。

实战案例#

响应式卡片组件#

.card-container {
container-type: inline-size;
}
.card {
display: grid;
gap: 1rem;
padding: 1rem;
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 8px;
}
.card-title {
font-size: 1.125rem;
font-weight: 600;
margin: 0;
}
.card-description {
color: #6b7280;
margin: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 中等宽度:水平布局 */
@container (min-width: 350px) {
.card {
grid-template-columns: 120px 1fr;
align-items: start;
}
.card-image {
aspect-ratio: 1;
}
}
/* 大宽度:更大图片,显示更多内容 */
@container (min-width: 500px) {
.card {
grid-template-columns: 200px 1fr;
padding: 1.5rem;
gap: 1.5rem;
}
.card-title {
font-size: 1.25rem;
}
.card-description {
-webkit-line-clamp: 3;
}
}

自适应导航#

.nav-container {
container-type: inline-size;
}
.nav {
display: flex;
align-items: center;
padding: 1rem;
}
.nav-logo {
font-weight: bold;
}
.nav-links {
display: none;
gap: 1rem;
margin-left: auto;
}
.nav-toggle {
margin-left: auto;
}
/* 容器足够宽时显示链接 */
@container (min-width: 600px) {
.nav-links {
display: flex;
}
.nav-toggle {
display: none;
}
}

仪表板小部件#

.widget-container {
container-type: inline-size;
}
.widget {
padding: 1rem;
background: white;
border-radius: 8px;
}
.widget-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.widget-title {
font-size: 0.875rem;
font-weight: 600;
margin: 0;
}
.widget-value {
font-size: 1.5rem;
font-weight: bold;
}
.widget-chart {
display: none;
height: 100px;
}
/* 小部件够大时显示图表 */
@container (min-width: 200px) {
.widget {
padding: 1.5rem;
}
.widget-value {
font-size: 2rem;
}
}
@container (min-width: 300px) {
.widget-chart {
display: block;
}
}

响应式表格#

.table-container {
container-type: inline-size;
overflow-x: auto;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #e5e7eb;
}
/* 小容器:隐藏次要列 */
.table .priority-low {
display: none;
}
@container (min-width: 500px) {
.table .priority-low {
display: table-cell;
}
}
/* 非常小的容器:卡片式布局 */
@container (max-width: 400px) {
.table,
.table tbody,
.table tr,
.table td {
display: block;
}
.table thead {
display: none;
}
.table tr {
margin-bottom: 1rem;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 1rem;
}
.table td {
border: none;
padding: 0.25rem 0;
}
.table td::before {
content: attr(data-label);
font-weight: 600;
margin-right: 0.5rem;
}
}

与媒体查询配合#

容器查询不是替代媒体查询,而是补充:

/* 媒体查询:整体布局 */
@media (min-width: 768px) {
.layout {
display: grid;
grid-template-columns: 250px 1fr;
}
}
/* 容器查询:组件级响应式 */
.sidebar {
container-type: inline-size;
}
@container (min-width: 200px) {
.sidebar-widget {
padding: 1.5rem;
}
}

最佳实践#

从组件层面思考#

/* 组件自己的容器定义 */
.card {
container-type: inline-size;
}
/* 组件内部的响应式规则 */
@container (min-width: 300px) {
.card-inner {
/* ... */
}
}

渐进增强#

/* 基础样式(不支持容器查询的浏览器) */
.card {
display: block;
}
/* 支持容器查询时 */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: flex;
}
}
}

避免过度嵌套#

/* 避免:过多嵌套容器 */
.a {
container-type: inline-size;
}
.b {
container-type: inline-size;
}
.c {
container-type: inline-size;
}
/* 推荐:在需要的层级定义容器 */
.card-grid {
container-type: inline-size;
}

浏览器兼容性#

浏览器支持版本
Chrome105+
Firefox110+
Safari16+
Edge105+

样式查询(@container style())目前仅 Chrome 111+ 支持。


容器查询是 CSS 响应式设计的重大突破。它让组件真正独立于放置位置,可以根据自身容器来调整布局。结合媒体查询,你可以构建既适应视口又适应容器的完美响应式体验。

这是 CSS 系列的最后一篇。希望这 15 篇文章能帮助你建立完整的 CSS 知识体系,从基础的选择器、盒模型,到现代的 Flexbox、Grid、容器查询,再到动画、变换、滤镜等视觉效果。CSS 是一门值得深入学习的技术,祝你在前端开发之路上越走越远!