[Web] CSS Container Queries 介紹
/ 8 min read
Updated: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 可以針對某個容器的條件套用樣式
基本流程分兩步:
- 在外層元素宣告它是一個 query container
- 在內層元素使用
@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%cqmin:cqi與cqb中較小的值cqmax:cqi與cqb中較大的值
例如讓標題隨容器寬度微調,但又限制最小與最大值
.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 增強版型
這樣元件就不需要依賴自己被放在哪個頁面,也能根據實際可用空間調整呈現方式