Tailwind CSS v4 Container Queries 完整教學:實現真正的元件級響應式設計
Container Queries vs Media Queries:差在哪?
老實說,我做前端做了這麼多年,Container Queries 是我覺得近年來最有感的 CSS 新功能,沒有之一。它解決的問題很本質,而且用過就回不去了。
Media Query 的根本問題
Media Query 是根據「視窗寬度」來決定樣式的。但問題是,在現代元件化開發的架構下,一個元件可能出現在頁面的任何位置——有時候佔滿全寬,有時候塞在一個 300px 寬的側邊欄裡。視窗寬度 1200px 的時候,你的元件可能只有 300px 寬,Media Query 根本幫不上忙。
這就是為什麼你會看到一堆專案裡有各種 workaround:傳 props 控制不同尺寸的樣式、用 ResizeObserver 監聽元素寬度、甚至用 JavaScript 動態加 CSS class。這些做法都不優雅,而且很難維護。
Container Query 如何解決這個問題
Container Query 的思路完全不同:樣式不再看視窗,而是看「父容器」的大小來決定。一個卡片元件放在寬的容器裡就顯示水平排列,放在窄的容器裡就自動切換成垂直堆疊。元件自己負責自己的響應式邏輯,完全不需要知道自己被放在頁面的哪個位置。
這才是真正的元件級響應式設計。如果你有在研究元件庫的設計哲學,可以看看我們之前寫的Tailwind CSS 元件庫比較,裡面有討論到不同元件庫對響應式的處理方式。
Tailwind CSS v4 的 Container Query 設定
Tailwind CSS v4 對 Container Queries 的支援是原生內建的,不需要額外安裝插件。這跟 v3 時代需要用 @tailwindcss/container-queries 插件的狀況不同,升級 v4 之後一切變得簡單很多。
定義容器
第一步是把某個元素標記為「容器」。在 Tailwind v4 裡,只要加上 @container 這個 utility class 就好:
<!-- 把外層 div 設為容器 -->
<div class="@container">
<div class="flex flex-col @md:flex-row gap-4">
<img src="..." class="w-full @md:w-1/3 rounded-lg" />
<div class="flex-1">
<h3 class="text-lg @lg:text-xl font-bold">卡片標題</h3>
<p class="text-gray-600">卡片內容...</p>
</div>
</div>
</div>
就是這樣,沒有複雜的設定。@container 加在外層元素上,裡面的子元素就可以用 @sm:、@md:、@lg: 等前綴來根據容器寬度套用不同樣式。
使用 @sm/@md/@lg 容器斷點
Tailwind v4 預設提供了以下容器斷點,這跟你熟悉的響應式斷點類似,但作用對象是容器而非視窗:
@xs— 容器寬度 ≥ 320px(20rem)@sm— 容器寬度 ≥ 384px(24rem)@md— 容器寬度 ≥ 448px(28rem)@lg— 容器寬度 ≥ 512px(32rem)@xl— 容器寬度 ≥ 576px(36rem)@2xl— 容器寬度 ≥ 672px(42rem)
注意這些數值跟一般的 sm:、md: 斷點是不同的。容器斷點的數值比較小,因為容器的寬度通常比整個視窗窄得多。這個設計其實很合理。
命名容器與巢狀容器
命名容器語法
當你的頁面上有多個容器時,你可能需要區分到底要參考哪個容器的寬度。這時候就需要命名容器了:
<div class="@container/sidebar">
<div class="@container/card">
<!-- 參考 sidebar 容器的寬度 -->
<p class="@md/sidebar:text-lg">...</p>
<!-- 參考 card 容器的寬度 -->
<p class="@sm/card:hidden">...</p>
</div>
</div>
語法是 @container/名稱 定義容器,@斷點/名稱: 參考特定容器。名稱可以是任何有意義的字串,取好名字能讓程式碼更容易理解。
處理巢狀容器場景
巢狀容器是比較容易踩坑的地方。預設情況下,如果沒有指定容器名稱,子元素會參考最近的父容器。在大多數情況下這是正確的行為,但如果你需要跨層參考,就一定要用命名容器。
我自己的經驗法則是:只要專案裡有兩個以上的容器,就全部加上名稱。多打幾個字可以省下很多除錯時間。
用 @theme 自訂容器斷點
Tailwind v4 新增了 @theme 語法,可以在 CSS 裡直接定義自訂斷點,不再需要改 JavaScript 設定檔:
@theme {
--container-3xs: 16rem; /* 256px */
--container-2xs: 18rem; /* 288px */
--container-xs: 20rem; /* 320px */
--container-sm: 24rem; /* 384px */
--container-md: 28rem; /* 448px */
--container-lg: 32rem; /* 512px */
--container-xl: 36rem; /* 576px */
--container-2xl: 42rem; /* 672px */
/* 自訂新的斷點 */
--container-3xl: 56rem; /* 896px */
--container-4xl: 72rem; /* 1152px */
}
這個功能讓你可以根據專案的設計規範來定義適合的斷點數值。不同專案的卡片寬度、側邊欄寬度都不一樣,預設的斷點不一定符合需求,自訂才是王道。
實戰範例
範例一:自適應卡片元件
這是最經典的使用場景。一張卡片可能出現在首頁的全寬區域,也可能出現在側邊欄:
<div class="@container">
<article class="
flex flex-col gap-3
@sm:flex-row @sm:gap-4
@lg:gap-6
bg-white rounded-xl shadow-sm p-4 @md:p-6
">
<img
src="/thumbnail.jpg"
alt="文章封面"
class="
w-full aspect-video object-cover rounded-lg
@sm:w-40 @sm:aspect-square
@lg:w-56
"
/>
<div class="flex-1 min-w-0">
<h3 class="
text-base font-semibold line-clamp-2
@md:text-lg
@lg:text-xl @lg:line-clamp-none
">文章標題在這裡</h3>
<p class="
mt-1 text-sm text-gray-500
hidden @sm:block
@lg:text-base @lg:mt-2
">文章摘要內容,在容器太窄的時候會隱藏...</p>
<div class="mt-2 flex items-center gap-2 text-xs text-gray-400">
<span>2026-03-18</span>
<span class="hidden @md:inline">• 8 min read</span>
</div>
</div>
</article>
</div>
這張卡片在窄容器裡會呈現垂直排列(圖片在上、文字在下),當容器變寬就自動切成水平排列,而且圖片大小、文字大小、間距都會跟著調整。完全不需要寫任何 JavaScript,也不需要知道卡片被放在頁面的哪裡。
範例二:Dashboard Widget
Dashboard 裡的 Widget 是 Container Query 的完美應用場景,因為使用者可能會調整面板大小:
<div class="@container/widget">
<div class="p-3 @md/widget:p-5">
<div class="flex items-center justify-between">
<h4 class="text-sm @md/widget:text-base font-medium">月營收</h4>
<span class="hidden @sm/widget:inline text-xs text-gray-400">
vs 上月
</span>
</div>
<div class="
mt-2 text-2xl font-bold
@lg/widget:text-3xl
">NT$1,234,567</div>
<div class="
mt-3 hidden
@md/widget:block
">
<!-- 圖表區域,只在容器夠寬時顯示 -->
<div class="h-24 @lg/widget:h-32 bg-gray-50 rounded"></div>
</div>
</div>
</div>
範例三:可收合側邊欄
側邊欄展開跟收合時,裡面的元件需要不同的排版方式。以前要用 JavaScript 偵測狀態來切換 class,現在用 Container Query 就能自動搞定:
<aside class="@container/nav transition-all duration-300"
:class="collapsed ? 'w-16' : 'w-64'">
<nav class="flex flex-col gap-1 p-2">
<a href="#" class="
flex items-center gap-3 px-3 py-2 rounded-lg
@xs/nav:justify-start
justify-center
">
<svg class="w-5 h-5 shrink-0">...</svg>
<span class="hidden @xs/nav:inline truncate">首頁</span>
</a>
<!-- 更多導航項目 -->
</nav>
</aside>
側邊欄收合時只顯示 icon,展開後文字自動出現。完全由 CSS 驅動,零 JavaScript 邏輯。
從 Media Query 遷移到 Container Query
如果你的專案已經在用 Tailwind 的響應式斷點(sm:、md: 等),遷移到 Container Query 其實很簡單,分三步:
- 找出「位置敏感」的元件:哪些元件會出現在不同寬度的容器裡?這些就是要遷移的對象。
- 在父層加上
@container:讓父元素成為容器參考點。 - 把
sm:改成@sm::注意斷點數值不同,可能需要微調。原本md:對應視窗 768px,容器@md只有 448px,所以你可能需要選用不同的斷點。
我的建議是不要一次全改,先從獨立性最高的元件開始,確認效果正確再逐步擴展。而且 Media Query 和 Container Query 可以共存,全域的版面配置用 Media Query、元件內部用 Container Query,這是目前最務實的做法。
瀏覽器支援度與 Fallback 策略
好消息是,到 2026 年,Container Queries 的瀏覽器支援度已經非常好了。Chrome、Firefox、Safari、Edge 的最新版本都完整支援。根據 Can I Use 的數據,全球支援率已經超過 93%。
如果你還需要支援舊版瀏覽器,可以搭配CSS :has() 選擇器做一些 fallback 處理。不過說實話,如果你的目標用戶主要用現代瀏覽器,直接用就好,不用太擔心相容性問題。
Tailwind v4 在編譯時會自動產生正確的 CSS @container 規則,你不需要寫任何原生 CSS。如果瀏覽器不支援,元素會顯示預設(沒有斷點前綴)的樣式,算是自然降級。
最佳實踐與常見陷阱
用了幾個月的 Container Queries 之後,我整理出以下幾點經驗:
- 不要所有元素都設成容器:只有真正需要作為參考點的外層元素才加
@container。過多的容器會增加瀏覽器的 layout 計算量。 - 注意 container-type 的影響:
@container預設會建立一個 inline-size containment,這表示容器的寬度不會被子元素撐開。大部分情況下這沒問題,但如果你遇到排版異常,這可能就是原因。 - 善用命名容器:專案一大起來,沒命名的容器會讓你分不清楚哪個斷點對應哪個容器。命名是零成本的好習慣。
- 搭配 CSS View Transitions API 使用:容器大小變化時加上過渡動畫,使用者體驗會好非常多。
- Design Token 統一:用
@theme統一定義容器斷點,跟設計師約定好數值,避免工程師各自用不同的斷點。
結語
Container Queries 是 CSS 發展史上一個里程碑級的功能,它終於讓元件的響應式設計變成元件自己的事,而不是頁面層級的全域控制。搭配 Tailwind CSS v4 的原生支援,現在使用的門檻已經低到不行了。
如果你還在用純 Media Query 來處理元件的響應式需求,我真心建議你花個半天時間試試 Container Queries。一旦用了,你會覺得以前那些 workaround 都是在浪費生命。前端的世界在變好,而 Container Queries 就是其中一個最好的證明。
繼續閱讀
Tailwind CSS 元件庫推薦:daisyUI、Headless UI、shadcn/ui 完整比較
Tailwind CSS 元件庫太多不知道怎麼選?這篇幫你比較 daisyUI、shadcn/ui、Headless UI 三大方案的差異。
相關文章
你可能也喜歡
探索其他領域的精選好文