同一個標題,在 27 吋螢幕上看起來大氣穩重,換到手機上卻可能把整個畫面塞滿,甚至把長字撐到溢出邊界。過去解法是寫一堆媒體查詢(media query),在每個斷點手動換字級,維護起來既瑣碎又容易漏。theme.json 流體排版(fluid typography)把這件事交給瀏覽器:你只定義最小與最大字級,WordPress 自動生成 CSS 的 clamp() 函式,讓文字隨視窗寬度平滑縮放,沒有跳動、也不必維護斷點。
這篇會帶你從零設定 theme.json 流體排版,包含全域開關、單一字級的 min/max、自訂視窗範圍、最常見的「設了卻沒生效」陷阱,以及搭配流動間距與無障礙下限的實作細節。範例都以區塊主題(block theme)為前提,貼進 theme.json 就能用。
什麼是 theme.json 流體排版
流體排版是讓字級在一段視窗寬度內連續縮放的排版方式,核心是 CSS 的 clamp() 函式。它取三個參數,分別是最小值、偏好值(通常含 vw 視窗單位)、最大值,瀏覽器會在範圍內挑出當下合適的尺寸。
WordPress 從 6.1 版開始內建這套機制。你只要在 theme.json 用宣告的方式給出字級的最小與最大,WordPress 就會自動算出中間的偏好值並產生 clamp(),不用自己手寫公式。
它跟傳統媒體查詢最大的差別在於「連續」與「跳動」。媒體查詢是在指定斷點瞬間切換字級,例如 599px 還是 24px、到 600px 突然跳成 32px,使用者拉動視窗時會看到明顯的跳格。clamp() 則是一條平滑曲線,從最小到最大之間每一個寬度都對應一個尺寸,過程沒有任何斷點。
下面用同一個標題在不同視窗寬度的表現對照,差異一目了然。
| 視窗寬度 | 媒體查詢做法 | 流體 clamp() 做法 |
|---|---|---|
| 375px(手機) | 24px | 24px(觸及最小值) |
| 600px | 32px(跳一格) | 約 28px(持續縮放) |
| 1024px | 40px | 約 36px(持續縮放) |
| 1200px | 48px(跳一格) | 約 40px(持續縮放) |
| 1600px | 48px | 48px(觸及最大值) |
除了體驗更平順,流體排版產生的 CSS 也更精簡。一套七階的字級系統,媒體查詢可能要寫上二十多條規則,流體做法每個字級只需要一條 clamp() 宣告。
theme.json 流體排版怎麼開啟
開啟流體排版只要一行:在 theme.json 的 settings.typography 底下把 fluid 設成 true。這是全域開關,預設是 false(關閉)。
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"typography": {
"fluid": true
}
}
}
開啟之後,WordPress 會把已註冊的字級預設值自動轉成 clamp()。以核心預設的字級為例,開啟前是固定的 px 值,開啟後會變成這樣的輸出(數值依版本與設定略有差異):
body {
--wp--preset--font-size--small: 13px;
--wp--preset--font-size--medium: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.852), 20px);
--wp--preset--font-size--large: clamp(22.041px, 1.378rem + ((1vw - 3.2px) * 1.983), 36px);
--wp--preset--font-size--x-large: clamp(25.014px, 1.563rem + ((1vw - 3.2px) * 2.413), 42px);
}
這裡有個容易被忽略的細節:small 維持固定的 13px 沒有變成 clamp()。原因是 WordPress 內建一個最小字級門檻 14px,低於這個門檻的字級不會被轉成流體值,避免文字在小螢幕上縮到難以辨識。這也是後面談無障礙時的重點。
每個字級預設都會對應一個 CSS 自訂屬性,命名格式是 --wp--preset--font-size--{slug},同時也會生成 .has-{slug}-font-size 的工具類別。這代表你在區塊編輯器裡選的字級、在 theme.json styles 區塊引用的字級,全都會自動套上流體行為,不需要逐處設定。
單一字級的 min 與 max 怎麼設定
全域開啟之後,下一步是替每個字級指定縮放範圍。做法是在 settings.typography.fontSizes 陣列裡,替每個字級物件加上 fluid 屬性,給定 min(最小)與 max(最大)。
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"typography": {
"fluid": true,
"fontSizes": [
{
"name": "Small",
"slug": "small",
"size": "1rem",
"fluid": false
},
{
"name": "Medium",
"slug": "medium",
"size": "1.25rem",
"fluid": {
"min": "1.125rem",
"max": "1.25rem"
}
},
{
"name": "Large",
"slug": "large",
"size": "2.25rem",
"fluid": {
"min": "1.75rem",
"max": "2.25rem"
}
}
]
}
}
}
每個欄位的角色要分清楚。size 是偏好值,同時當作不支援 clamp() 的瀏覽器的後備值(fallback),現今幾乎所有主流瀏覽器都支援 clamp(),但保留它仍是好習慣。fluid.min 是這個字級在最窄螢幕的下限,fluid.max 是在最寬螢幕的上限,WordPress 用這兩個值去算出實際的 clamp() 範圍。
縮放幅度的設計有個原則:小字級的 min 與 max 差距要小、大字級的差距可以大。內文這類本來就接近閱讀下限的字,跨裝置變化不該太大,否則手機上會難讀;標題在手機上則需要明顯縮小,才不會把版面撐爆。上面的範例 Medium 從 1.125rem 到 1.25rem 變化很小,Large 從 1.75rem 到 2.25rem 變化較大,就是這個道理。
怎麼自訂流體排版的視窗範圍
WordPress 預設的流體縮放區間是視窗寬度 768px 到 1600px。低於 768px 時字級停在最小值、高於 1600px 時停在最大值,中間才會連續縮放。這個預設不一定符合你的設計,從 6.4 版開始可以自訂這個範圍。
做法是把 fluid 從 true 改成一個物件,給定 minViewportWidth 與 maxViewportWidth。
{
"version": 2,
"settings": {
"typography": {
"fluid": {
"minViewportWidth": "320px",
"maxViewportWidth": "1440px"
}
}
}
}
這段設定告訴 WordPress 從 320px 開始縮放、到 1440px 抵達最大值。320px 以下停在最小、1440px 以上停在最大。調整這組數值會改變縮放曲線的斜率:範圍拉得窄,縮放會比較急遽;範圍拉得寬,縮放會比較平緩。
選範圍時可以對照實際的目標裝置。台灣讀者主流的手機寬度大多落在 375px 到 430px、平板與桌機則往 768px 以上分布,把最小視窗對到常見手機寬度、最大視窗對到你的內容最寬處(通常接近 contentSize),縮放曲線會最貼合真實閱讀情境。
設了流體排版卻沒生效,問題出在哪
最常見的狀況是字級在瀏覽器裡仍顯示為固定值、沒有變成 clamp()。九成是因為全域開關沒打開。記住一個關鍵規則:settings.typography.fluid 必須在根層級設為 true(或設為含視窗範圍的物件),單一字級上的 fluid 物件才會生效。只在某個字級裡寫 fluid 的 min/max、卻沒開全域開關,那個設定會被直接忽略。
第二個常見錯誤是屬性名稱寫錯。正確的屬性名是 fluid,不是 fluidSize 或其他變體。網路上部分教學文寫成 fluidSize,那是錯的,照抄會無效。要確認自己的版本,可以查官方的 theme.json schema,或直接看下一段的驗證方法。
第三種狀況是字級低於 14px 的內建門檻而沒被轉換,這不是錯誤而是預期行為,前面已經說明。如果你確實需要更小的流體字,得另外用自訂 CSS 處理,而不是硬要 WordPress 轉換。
驗證設定有沒有生效最直接的方法,是在前台用瀏覽器開發者工具檢視 <body> 或對應區塊的 CSS 自訂屬性。看到 --wp--preset--font-size--medium 的值是一串 clamp(),就代表流體排版已經套上;如果還是純粹的 px 或 rem,就回頭檢查全域開關與屬性名稱。
哪些字級該關掉流體縮放
不是每個字級都適合縮放。標籤、圖說、按鈕文字這類小型 UI 文字,一旦在手機上被縮得更小,就會掉到難以閱讀的程度。這種情況要替個別字級把 fluid 設成 false,讓它無論視窗多窄都維持固定大小。
{
"name": "Caption",
"slug": "caption",
"size": "0.875rem",
"fluid": false
}
實務上的判斷是:本來就接近可讀下限的小字,傾向關掉流體;標題、副標、大型展示文字這類有足夠縮放空間的,才開流體並給較大的 min/max 差距。流體與固定字級可以在同一份 fontSizes 裡混用,全域 fluid 仍維持 true,個別字級用 fluid: false 各自退出即可。
流動間距怎麼跟流體排版搭配
流體排版很少單獨上場,通常會搭配流動間距(fluid spacing)一起做,整個版面的呼吸感才會一致。WordPress 同樣從 6.1 版起支援,設定位置在 settings.spacing.spacingSizes,每個間距預設也接受跟字級相同的 fluid 結構。
{
"version": 2,
"settings": {
"spacing": {
"spacingSizes": [
{
"name": "Medium",
"slug": "50",
"size": "1.5rem",
"fluid": {
"min": "1rem",
"max": "1.5rem"
}
},
{
"name": "Large",
"slug": "70",
"size": "3rem",
"fluid": {
"min": "2rem",
"max": "3rem"
}
}
]
}
}
}
設好之後,在 theme.json 的 styles 區塊用 var:preset|spacing|{slug} 的語法引用這些間距,整個主題的留白就會跟著視窗一起縮放。比較推薦的做法是手動定義 spacingSizes 並逐項給 fluid 參數,而不是依賴 settings.spacing.spacingScale 的自動級距,因為自動級距無法替每一階指定 min/max,控制力較弱。
間距系統有個進階但實用的設定值得一提:useRootPaddingAwareAlignments。把它設為 true、再讓根層級的 padding 引用流動間距預設,整頁左右留白就會隨視窗縮放,同時讓 full-width 對齊的區塊正常突破留白、撐滿整個視窗。桌機上內容有寬裕的邊距、手機上邊距自動收緊以爭取閱讀空間,全程不需要一條媒體查詢。
無障礙與測試,上線前該確認什麼
流體排版最容易在「最小值」這端翻車。把字級的 min 設得太小,手機上的內文會掉到難以閱讀的程度。一個保守的原則是內文不要低於 14px(約 0.875rem),標題的最小值也要維持清楚的層級感,不要在手機上縮到跟內文難以區分。WordPress 內建的 14px 門檻就是在守這條線,自己設 min 時也該沿用同樣的標準。
測試方式跟傳統響應式不同。因為流體縮放是連續的,問題可能出現在你沒想到要檢查的中間寬度,光看 375px、768px、1024px 這幾個固定點並不夠。正確做法是在瀏覽器開發者工具的響應式模式裡,把視窗寬度從最小(例如 320px)緩慢拖到最大(例如 1440px 或更寬),全程盯著文字與間距的變化,特別留意有沒有字在某個寬度突然太大或太小、有沒有長字溢出、標題與內文的層級會不會在某段寬度糊在一起。
上線前可以照這份清單逐項確認:全域 fluid 已開啟並設定合理的視窗範圍、每個該縮放的字級都有明確的 fluid.min 與 fluid.max、不該縮放的小字已用 fluid: false 退出、間距預設也都帶 fluid 參數、根層級 padding 引用流動間距且 useRootPaddingAwareAlignments 為 true、內文最小值不低於可讀下限、在 320px 到 1440px 之間連續拖曳測試都正常。每一項都對應一種實際會發生的破版情境,漏掉任何一項都可能在某個寬度露餡。
掌握 theme.json 流體排版的關鍵,是把它當成一套「定義約束、交給瀏覽器算」的系統,而不是逐裝置調字級。先用全域 fluid 打開引擎,再替每個字級給合理的 min/max 與一致的縮放比例,搭配流動間距讓留白同步呼吸,最後守住最小值的可讀性並用連續拖曳測試把關。把這幾步落實,你的區塊主題就能在從手機到大螢幕的任何寬度下,自動維持穩定又好讀的排版,而且整份設定只集中在 theme.json 一處,往後維護只動這一個檔案。