功能说明
卡片外层 .card-shell 声明 container-type: inline-size 与 container-name: card,内部 .card 默认纵向排列图片与文字。当容器宽度达到 30rem 时切换为两列,图片列使用 minmax 限制最小宽度;达到 48rem 时进一步调整比例。标题字号用 clamp 配合 2cqw,使文字随容器宽度平滑变化。不支持容器查询的浏览器退回基于视口宽度的媒体查询。
关键代码
container-type: inline-size 建立行内尺寸查询上下文,同时带来布局、样式和尺寸包含,子元素不能反过来撑大容器。@container card (min-width: 30rem) 查询的是最近的同名容器,而不是视口。grid-template-columns 使用 minmax(12rem, 2fr) 3fr,保证图片不会缩到不可读。cqw 是容器宽度的百分比单位,仅在查询容器后代中有效。@supports not (container-type: inline-size) 提供旧浏览器回退。
使用注意
容器不能查询自身,查询规则必须作用于容器的后代。给容器设置固定高度会与尺寸包含冲突,应让内容决定高度。卡片放在侧栏、主栏或弹窗中时会自动适配,这是相对媒体查询的优势。命名容器可以嵌套,但同名时以最近祖先为准。回退方案仍需在真实旧浏览器中检查,因为 @supports 的解析行为可能因版本而异。
验证方式
在浏览器开发者工具中拖动放置卡片的侧栏宽度,视口保持不变时卡片应在 30rem 与 48rem 处切换布局,标题字号也随之变化。把卡片移入更宽的弹窗,确认断点跟随容器而非窗口。禁用容器查询支持或使用旧浏览器,检查媒体查询回退是否仍能给出可用的两列布局。
代码示例
/* 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;
}
}
}