概念概述
CSS 容器查询(CSS Container Queries)指的是:容器查询依据父容器而非视口尺寸为组件设定样式,实现真正可复用、能在不同布局容器中自适应的响应式组件。
从更严格的定义看,A container query is a conditional CSS rule that applies when a designated container element meets a size or style condition, letting a component adapt to the space it occupies. 该概念在工程实践中的价值,在于它能帮助团队用统一的语言描述一个具体的质量属性或设计约束,从而在评审与协作中减少歧义。
为什么需要它
They matter most for design systems whose components appear in sidebars, cards, and full-width layouts within the same page. 它与 Third-Party Cookies and Storage Partitioning、Subresource Integrity (SRI)、Preconnect and DNS-Prefetch Hints 等概念经常一起出现,用于解决相近的工程问题。理解它的适用场景,有助于在多种备选方案之间做出有依据的取舍,而不是凭直觉选择技术方案。
常见误解
- container queries measure the viewport like media queries
- an element becomes a query container automatically
需要避免的反模式
- creating many nested size containers without need
- querying a container for changes that originate on the queried element itself
实践要点
- A query targets the nearest ancestor declared as a containment context, so authors set container-type on the wrapper whose width should drive the component’s layout.
- Size containment means the container’s size cannot depend on the content it queries, and circular dependencies are resolved by ignoring self-referential conditions.
- Style container queries extend the idea to custom property values, allowing a component to respond to a theme token set by an ancestor rather than to raw dimensions.
参考来源
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries
代码示例
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (min-width: 400px) {
.card {
grid-template-columns: 1fr 1fr;
}
}
/* Markup contract:
<article class="card-shell"><div class="card">
<img class="card__media" src="/desk.jpg" alt="Standing desk">
<div class="card__body">
<h2 class="card__title">Standing desk</h2>
<p>Height-adjustable frame with a memory preset.</p>
</div>
</div></article> */
.card-shell {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
gap: var(--space-3, 1rem);
padding: var(--space-4, 1.5rem);
border: 1px solid var(--color-border, #d4d4d8);
border-radius: var(--radius-lg, 0.75rem);
background: var(--color-surface, #ffffff);
}
.card__media {
inline-size: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: calc(var(--radius-lg, 0.75rem) - 0.25rem);
}
.card__title {
margin-block: 0;
font-size: 1.15rem;
text-wrap: balance;
}
/* The query measures .card-shell, not the viewport. */
@container card (min-width: 30rem) {
.card {
grid-template-columns: minmax(12rem, 2fr) 3fr;
align-items: start;
}
.card__title {
font-size: clamp(1.15rem, 1rem + 2cqw, 1.6rem);
}
}
@container card (min-width: 48rem) {
.card {
grid-template-columns: minmax(14rem, 1fr) 2fr;
}
}
/* Viewport fallback for browsers without container query support. */
@supports not (container-type: inline-size) {
@media (min-width: 48rem) {
.card {
grid-template-columns: minmax(14rem, 1fr) 2fr;
align-items: start;
}
}
}