skip to content
BlogZzz
Table of Contents

前言

以前做 responsive layout,大多會先想到 media queries

media queries 很適合處理整個 viewport 的變化,例如手機、平板、桌機各自套不同版型

但在元件化開發裡,很多 UI 問題其實不是「螢幕多寬」,而是「這個元件現在被放在多寬的容器裡」

同一張 card 可能出現在首頁寬版區塊,也可能出現在 sidebar、grid item、modal 或 dashboard widget 裡 如果只靠 viewport 寬度判斷,元件就會跟頁面結構綁得太緊

CSS Container Queries 解決的就是這個問題:讓元件可以根據外層容器的尺寸來改變自己的樣式

Media Queries 的限制

先看一個常見情境

@media (min-width: 768px) {
.article-card {
display: grid;
grid-template-columns: 160px 1fr;
}
}

這段 CSS 的意思是:當 viewport 大於 768px,card 就改成左右排列

問題在於,viewport 變寬不代表 card 自己也變寬

例如:

  • card 在桌機版 sidebar 裡,實際寬度可能只有 280px
  • card 在 dashboard 的小 widget 裡,實際寬度可能也不適合左右排列
  • card 被拿到別的頁面重用時,原本的 breakpoint 可能完全不適用

這時候元件需要的不是問「瀏覽器多寬」

而是問「我被放進去的那個容器多寬」

Container Queries 是什麼

Container Queries 讓 CSS 可以針對某個容器的條件套用樣式

基本流程分兩步:

  1. 在外層元素宣告它是一個 query container
  2. 在內層元素使用 @container 寫條件樣式
.card-shell {
container-type: inline-size;
}
.article-card {
display: grid;
gap: 16px;
}
@container (width >= 480px) {
.article-card {
grid-template-columns: 160px 1fr;
}
}

這段的重點是 @container 看的是最近一層可查詢容器的寬度,而不是 viewport 的寬度

所以同一個 .article-card,放在不同容器中可以呈現不同版型

container-type 的作用

要讓某個元素可以被 @container 查詢,需要先設定 container-type

最常用的是 inline-size

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

inline-size 代表查詢容器的 inline 軸尺寸 在一般水平書寫模式下,可以先理解成查詢寬度

另一個值是 size

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

size 可以查詢 inline 與 block 兩個方向,也就是寬度與高度 但它會套用更強的 size containment,使用上限制比較多

實務上,如果只是做卡片、列表、區塊版型切換,通常先用 inline-size 就好

用 container-name 避免選錯容器

預設情況下,@container 會找最近的 query container

如果畫面裡有多層容器,或者你想明確指定某一層,可以加上 container-name

.product-section {
container-name: product-list;
container-type: inline-size;
}
@container product-list (width >= 720px) {
.product-card {
grid-template-columns: 220px 1fr;
}
}

也可以用 shorthand 寫成:

.product-section {
container: product-list / inline-size;
}

命名容器的好處是可讀性更高,尤其是元件被放在 layout、section、card shell 多層結構裡時,比較不容易誤判查詢目標

實作範例:可重用的文章卡片

假設我們有一張文章卡片

<section class="post-list">
<article class="post-card">
<img src="/cover.jpg" alt="文章封面" />
<div>
<h2>CSS Container Queries 入門</h2>
<p>讓元件根據容器尺寸改變版型,而不是只依賴 viewport</p>
</div>
</article>
</section>

先讓外層列表成為 query container

.post-list {
container: post-list / inline-size;
}
.post-card {
display: grid;
gap: 12px;
}
.post-card img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}

接著依照容器寬度切換 card 版型

@container post-list (width >= 520px) {
.post-card {
grid-template-columns: 180px 1fr;
align-items: start;
}
}
@container post-list (width >= 840px) {
.post-card {
grid-template-columns: 240px 1fr;
gap: 20px;
}
}

這樣做之後,.post-card 不需要知道自己在首頁、分類頁、搜尋頁或側欄

只要外層容器夠寬,它就切成左右排列 容器不夠寬,就維持上下排列

Container Query Units

Container Queries 也提供一組跟容器尺寸有關的單位

常見單位如下:

  • cqw:query container 寬度的 1%
  • cqh:query container 高度的 1%
  • cqi:query container inline size 的 1%
  • cqb:query container block size 的 1%
  • cqmincqicqb 中較小的值
  • cqmaxcqicqb 中較大的值

例如讓標題隨容器寬度微調,但又限制最小與最大值

.post-card h2 {
font-size: clamp(1.125rem, 4cqi, 1.75rem);
}

這種寫法比單純用 vw 更貼近元件本身 因為它參考的是容器尺寸,不是整個 viewport

常見注意事項

第一個注意事項是:不能查詢自己

@container 套用的是容器內部的後代元素 所以通常會需要一層 wrapper 當 query container

<div class="card-shell">
<article class="article-card">...</article>
</div>

第二個注意事項是:不要把所有 breakpoint 都搬成 container query

media queries 仍然適合處理全站層級的變化,例如主導覽、整體欄位數、頁面寬度、裝置能力

Container Queries 比較適合處理元件層級的變化,例如 card、toolbar、form group、widget、summary panel

第三個注意事項是:先設計預設樣式,再漸進增強

.profile-card {
display: grid;
gap: 12px;
}
@container (width >= 480px) {
.profile-card {
grid-template-columns: auto 1fr;
}
}

預設樣式應該在小容器下也能正常閱讀 再用 @container 加上較寬容器才需要的排列

什麼時候該用

適合使用 Container Queries 的情境:

  • 同一個元件會出現在不同寬度的區塊
  • 元件被放在 grid、sidebar、modal、dashboard 等不同 layout 中
  • 元件自己的可用空間比 viewport 更重要
  • 想降低 component CSS 對頁面 breakpoint 的依賴

不一定需要使用的情境:

  • 只是在做整頁手機版與桌機版切換
  • layout 只有單一固定上下文
  • breakpoint 本來就跟 viewport 強相關
  • 用 flex 或 grid 自然換行就已經足夠

簡單判斷方式是:如果你腦中想問的是「頁面多寬」,用 media queries

如果你想問的是「這個元件現在有多少空間」,用 Container Queries

結語

Container Queries 讓 CSS 的 responsive 思維從頁面層級往元件層級移動

它不是取代 media queries,而是補上 media queries 在元件重用上的缺口

實務上可以從 card、列表項目、dashboard widget 這類元件開始導入 先保留小容器下的預設樣式,再針對寬容器用 @container 增強版型

這樣元件就不需要依賴自己被放在哪個頁面,也能根據實際可用空間調整呈現方式

參考