多數關於 theme.json 的教學,幾乎都繞著同一件事打轉,也就是怎麼設定留白與間距比例尺。這個檔案能做的其實遠不只如此,它是整個區塊主題的中樞設定檔,從色票、字體、版面寬度,到後台介面裡哪些控制項會出現、哪些會被隱藏,全部由它一份 JSON 決定。
如果你只把 theme.json 當成「間距設定檔」,遇到色彩選色器怎麼跑出來的、字體選單為什麼是空的、某個區塊的邊框控制忽然多了幾個選項,大概率會一頭霧水,因為問題出在 theme.json 的另外兩個部分。theme.json 是 WordPress 區塊主題用來集中管理外觀設定的單一 JSON 檔案,settings 決定後台介面上「使用者能不能調」,styles 決定「調成什麼實際數值」,這兩層分工清楚,才是它真正被設計出來要解決的問題。沒把這層分工搞懂,遇到設定衝突或介面缺控制項時很難抓到頭緒,也很難看懂官方文件裡動輒五、六層的巢狀結構。
theme.json 是什麼?settings 與 styles 分工出兩層控制
theme.json 是 WordPress 區塊主題用來集中管理外觀設定的一份 JSON 檔案,放在主題資料夾最外層,官方文件把它的頂層結構定義成 7 個欄位:$schema、version、settings、styles、customTemplates、templateParts、patterns。
這 7 個欄位裡最重要、也最容易被搞混的是 settings 與 styles 這一對。settings 負責決定「後台編輯介面上會不會出現某個控制項、預設值是什麼」,例如要不要顯示自訂顏色選色器、字體選單裡有哪些字體可以挑;styles 負責決定「這些控制項調出來的實際數值套用在網站上是什麼」,例如全站文字顏色實際是哪一個色碼、標題字級實際是多少像素。兩者分開設計的用意很直接,同一份設定一邊管「能不能調」,一邊管「調成什麼」,職責不會混在一起。

version 這個欄位常被忽略,卻直接影響主題吃不吃得到新功能。目前最新的 schema 是版本 3,WordPress 6.6 才引入;在那之前主題常見寫的是版本 2。這個數字最好顯式宣告在 theme.json 裡,版本 1 是好幾年前的舊規格,主題若沒清楚跟著新版規格走,新版才有的屬性可能完全讀不到,也不會出現在後台介面上。
套用優先權從高到低依序是使用者、主題與核心
theme.json 的規格刻意設計成同時適用於三種來源:WordPress 核心本身、主題的 theme.json,以及使用者在網站編輯器(Site Editor)裡自己調整過的設定。三方都可能對同一個屬性各自訂一個值,實際生效的順位由高到低是使用者、主題、核心,使用者透過介面調整過的設定會蓋過主題寫在 theme.json 裡的值,主題的值又會蓋過 WordPress 內建的預設值。
這也回答了一個常讓人納悶的問題,為什麼使用者在後台把某個顏色調過一次之後,主題日後更新 theme.json、改了那個顏色的預設值,線上網站卻完全沒有變化。原因是同一個屬性若同時被主題與使用者設定,WordPress 只會把使用者那一份排進最終生效的樣式表,不會兩份都排。這樣做既避免重複 CSS 造成的樣式衝突,也確保使用者自己調整過的東西不會被主題更新悄悄蓋掉。

appearanceTools 一個開關就能同時打開七組設計控制
settings.appearanceTools 是一個特殊的布林值開關,設成 true 就等於一次打開 7 組最常被主題用到的設計控制,不必在 theme.json 裡把每個子屬性都手動列成 true。官方文件列出這 7 組控制範圍分別是:背景(backgroundImage、backgroundSize)、邊框(color、radius、style、width)、顏色(link)、尺寸(aspectRatio、height、minHeight、minWidth、width)、定位(sticky)、間距(blockGap、margin、padding)、字體(lineHeight)。間距在這裡只是這 7 組其中一項的涵蓋範圍。

在 theme.json 出現以前,WordPress 主題要打開這些控制項,靠的是在 functions.php 裡寫一整排 add_theme_support()。這套舊寫法跟 theme.json 的屬性是一一對應的關係,例如 disable-custom-colors 等同於把 color.custom 設成 false、editor-color-palette 等同於用 color.palette 提供一組色票陣列、appearance-tools 這個標籤本身就對應 appearanceTools 設成 true、link-color 等同於把 color.link 設成 true。理解這組對照,能幫舊主題要轉成 theme.json 寫法的人少走很多冤枉路。
邊框顏色、圓角、樣式與寬度,四個獨立開關預設全部關閉
settings.border 底下有 4 個獨立的布林值,各自對應後台介面上一組控制項:color 決定要不要顯示邊框顏色選色器、radius 決定要不要顯示圓角控制、style 決定要不要顯示實線、虛線、點線的樣式選擇、width 決定要不要顯示邊框寬度輸入框。這 4 個屬性預設值全部是 false,主題如果完全沒有設定 settings.border,使用者在編輯器裡不會看到任何邊框控制。
自 WordPress 6.3 起,color、style、width 這三者的行為變成綁在一起,只要其中一個設成 true,另外兩個也會一併出現在介面上,不再各自獨立顯示。radius 沒有被綁進這套連動邏輯,可以單獨開關。另外要提醒的是,Button 區塊的圓角控制不受 radius: false 影響,即使全域把圓角關掉,Button 區塊的圓角選項仍然會顯示,這是核心區塊自己額外做的例外設定。
色票、漸層與雙色調各自是一組陣列,寫進顏色設定就能被選單讀到
settings.color 底下可以註冊 3 種色彩預設,分別是 palette(單色)、gradients(漸層)、duotone(雙色調濾鏡),三種都是物件陣列,每個物件需要固定欄位才能被 WordPress 正確讀取。註冊之後,這些色彩會自動出現在編輯器的色彩選擇器裡,供使用者直接點選套用,不必自己輸入色碼。
palette 陣列裡每個物件要有 3 個必填欄位:color(合法的 CSS 色彩值)、name(顯示在介面上給人看的標籤)、slug(機器可讀的代碼,用來生成 CSS 變數與 class)。gradients 陣列的物件結構對應是 gradient、name、slug。註冊完成後,WordPress 會依 slug 自動產生對應的 CSS 變數與 class:CSS 變數統一命名成 --wp--preset--{type}--{slug}(例如色票 slug 是 contrast,就會產生 --wp--preset--color--contrast),對應的 class 則命名成 .has-{slug}-{type}(例如 .has-contrast-color、.has-contrast-background-color)。這套自動生成的命名規則,是主題開發者在寫自訂 CSS 時能不能對得上顏色設定的關鍵。
自訂色彩、自訂漸層與自訂雙色調三個開關,預設都是開啟的
色票怎麼登記是一回事,使用者能不能自己跳過色票、隨意調色又是另一回事,後者由 3 個開關管:settings.color.custom、customGradient、customDuotone,分別控制使用者能不能自訂顏色、自訂漸層背景、自訂雙色調濾鏡。這 3 個開關預設值都是 true,主題如果什麼都不設定,使用者在色彩選擇器裡除了挑主題登記好的色票,一樣可以用色碼輸入框自己調一個全新的顏色。主題若想強迫使用者只能從自己登記的色票裡選,就要把這 3 個開關都設成 false。
另外還有一組容易搞混的開關:defaultPalette、defaultGradients、defaultDuotone,同樣預設都是 true。不過這 3 個管的是 WordPress 核心內建的預設色票、漸層、雙色調要不要出現在選單裡,並不是使用者能不能自訂。就算主題把這 3 個關掉,核心的預設色票仍然會生成對應的 CSS 變數,維持跟舊版內容的相容性,只是不會顯示在選單裡讓使用者挑選。
base 與 contrast 是色票代稱的慣例寫法
顏色的命名沒有官方強制規則,theme.json 裡的 slug 想取什麼都可以,但業界確實有一套事實上的慣例存在。這套慣例其實是由 WordPress 官方預設佈景主題 Twenty Twenty-Three 立下的,習慣把 base 用在網站背景色、contrast 用在文字色上。跟著這個慣例命名,好處不只是省事,更在於這樣的色票在使用者更換佈景主題、或切換樣式變化時,相容性會比較高。
外掛作者在寫需要依賴主題色彩的功能時,也能直接假設一個佈景主題大概率會有 base 與 contrast 這兩個 slug 可以當退回色使用,不必猜測每個主題各自取的命名。這是一個很小、卻能讓整個生態系互相對得上的約定。
字體設定橫跨系統字體與自架 Web Font,兩種宣告方式都在 fontFamilies 底下
settings.typography.fontFamilies 是用來註冊「使用者在字體選單裡能挑到哪些字體」的地方,預設是一個空陣列,主題必須主動把想開放的字體逐一寫進去,WordPress 不會自動生成任何預設字體選項。可以註冊的字體分成兩種:只宣告系統既有的字體堆疊,不需要額外檔案;或者進一步用 fontFace 屬性,把主題自己打包的 Web Font 檔案綁進來。
兩種寫法的差別只在於物件裡有沒有多加一個 fontFace 屬性。只想開放系統字體,不用碰它;要綁定自架的字體檔案,才需要加上去。
fontFamilies 陣列三個必填欄位,決定字體選單顯示的名稱
fontFamilies 陣列裡的每個物件,都要填 3 個必填欄位。name 是顯示在字體選單上、給使用者看的可翻譯標題;slug 會被接到 CSS 變數 --wp--preset--font-family--{slug} 後面,是機器讀取用的代碼;fontFamily 則是合法的 CSS font-family 值,通常寫成一整串字體堆疊,讓瀏覽器依序嘗試套用。
slug 建議取語意化的命名,例如 primary、secondary,而不是直接把目前字體的名稱寫死進去。這樣做的好處是,日後想換一套字體時,只需要改 fontFamily 這個值本身,slug 跟它牽連的 CSS 變數、class 名稱都不用動,也比較不會在切換子佈景主題或樣式變化時對不上。
fontFace 讓字體檔案跟著主題走,不必外連 Google Fonts
在 fontFamilies 物件裡加上 fontFace 這個可選屬性,就能把主題自己打包的本地字體檔案(例如 .woff2)跟著主題一起發佈,透過 file:./路徑 語法引用,WordPress 會依這個宣告產生對應的 @font-face 規則。比起外連 Google Fonts,這種做法能少一次外部網路請求,對頁面速度與隱私都比較有利。
fontFace 陣列裡的每個物件,對應的是 @font-face 規則的各個描述子:fontFamily、fontWeight(可以寫成範圍,例如 300 800 代表這是一支可變字重字體)、fontStyle(normal 或 italic)、fontStretch,以及 src(字體檔案的網址陣列,其中可以用 file:./assets/fonts/xxx.woff2 引用主題內建的檔案,雖然支援多種格式,實際上只需要提供一種就夠)。
固定字級與流動字級 fluid 縮放的差別
settings.typography.fluid 是一個全域開關,預設值是 false,代表字級是靜態的,不管螢幕多寬,數值都不會變。設成 true 之後,WordPress 會用 CSS 的 clamp() 函式,讓字級隨著視窗寬度在一個最小值與最大值之間縮放,例如原本固定 20px 的 medium 字級,流動化後會變成 clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.852), 20px) 這樣的寫法。
這個全域開關可以被個別字級覆寫。fontSizes 陣列裡每個字級物件,也能各自帶一個 fluid 屬性,寫成 false 就強制這個字級維持靜態,寫成 {min, max} 就自訂它自己的縮放範圍,不必跟著全域設定走。另外有一個固定的門檻值,14px 是預設的最小字級門檻,低於這個門檻的字級(例如 13px 的 small)即使全域開了流動縮放,也還是會維持靜態值,不會跟著視窗寬度變動。
contentSize 與 wideSize 兩個數值劃出內容欄與寬版欄的邊界
settings.layout 底下只有 2 個屬性:contentSize 與 wideSize。contentSize 定義的是一般內容欄的最大寬度,也就是文章正文預設能撐開的寬度;wideSize 定義的是「寬版對齊」能撐開的最大寬度,介於一般內容欄與滿版之間。這兩個數值實際會影響 Site Editor 裡「內容寬度」與「寬版對齊」這兩個版面選項的行為。
contentSize 的數值該抓多大,有個經驗法則可以參考,每行文字維持在 45 到 75 個字元之間是普遍被認為好讀的範圍,實際數字還是要看主題用的字型與字級搭配。wideSize 必須巢狀在已經設定了內容寬度的區塊裡才有意義,數值上應該比 contentSize 大,但不需要大到滿版寬度,滿版另外有獨立的設定管。如果整個版型本身沒有多餘空間讓區塊「跳出」原本的容器,那也不必特地設定 wideSize。
settings.custom 陣列會被轉成 –wp–custom– 開頭的 CSS 變數
settings.custom 跟前面幾節不一樣,它不是一個有固定結構的屬性,而是完全開放給主題自訂鍵值的地方。寫進去的每一組鍵值,WordPress 都會依鍵名自動生成對應的 CSS 自訂屬性,讓主題在自己的樣式表裡直接引用,不必另外手寫一份 CSS 變數定義。
命名規則有幾個要記住的地方。駝峰式命名會被自動轉換成連字號分隔,例如寫 lineHeight 會生成 --wp--custom--line-height;鍵名裡出現的數字,處理方式跟大寫字母相同,例如 m16 會被轉成 m-16。巢狀物件則會依層級把變數名稱疊加起來,例如 custom.lineHeight.md 這樣的結構,最後生成的變數會是 --wp--custom--line-height--md。

在 styles 裡要引用這些自訂變數,有兩種寫法都能用:一種是 theme.json 專用的 var:custom|line-height|md 語法,另一種是原生 CSS 的 var( --wp--custom--line-height--md )。如果是在主題自己的 style.css 裡要用同一組值,同樣直接寫原生 CSS 變數名稱即可,兩邊引用的是同一份設定,不會對不上。
同一個屬性在特定區塊表現不同,靠的是 settings.blocks 逐一覆寫
大多數 theme.json 設定預設是全域生效的,所有支援某個屬性的區塊,套用的都是同一份設定。但透過 settings.blocks.{命名空間/區塊代稱},可以針對單一區塊覆寫全域值,而且是完全覆寫、不是疊加,區塊層級一旦寫了設定,就以它為準。
官方文件用 Cover 區塊示範這個機制怎麼運作:先在全域的 settings.color.palette 裡註冊一組藍白色票,slug 分別叫 base、contrast;接著在 settings.blocks["core/cover"].color.palette 裡,用同樣的 base、contrast 這兩個 slug,但改註冊成橘色系。套用之後,只有 Cover 區塊的色彩選擇器會顯示橘色系選項,其他所有區塊看到的仍然是全域設定的藍白色票。
要對某個區塊做這種覆寫,得同時知道它的命名空間與代稱,兩者組合起來才是完整的鍵名,例如核心區塊的命名空間一律是 core,Cover 區塊代稱是 cover,組合起來就是 core/cover。第三方外掛做的區塊,命名空間跟代稱不一定好猜,最保險的做法是直接查那個區塊的 block.json 檔案,裡面會寫明它註冊時用的完整名稱。
core/button 與 core/pullquote,兩個區塊內建的邊框例外設定
WordPress 核心自己也用了這套機制,而且是預先寫在核心的預設 theme.json 裡:core/button 的 border.radius 被設成 true,core/pullquote 的 border.color、radius、style、width 全部被設成 true。這些覆寫的目的是向下相容舊版功能,並不是主題開發者自己設的。
如果發現 Button 或 Pullquote 這兩個區塊的邊框行為,跟自己在全域 settings.border 裡設定的不一樣,不必懷疑是自己的全域設定寫錯,該去 settings.blocks 裡找答案。真的想讓這兩個區塊的邊框行為跟全域一致,可以在自己的 theme.json 裡用同樣的 settings.blocks 結構,把想要的設定覆寫回去。
styles 分成全站、元素與區塊三個層級,範圍從大到小依序收斂
styles 是 theme.json 的另一個頂層屬性,內部可以再細分成 3 個層級,範圍從大到小依序收斂。最外層是根層級,直接影響全站,例如根層級寫的 color.text、color.background 會套用到整個網站;中間一層是 styles.elements,針對特定的 HTML 元素,例如標題、連結、按鈕;最內層是 styles.blocks,針對特定的區塊。同一個屬性如果在多個層級都被設定,範圍較小的層級會蓋過範圍較大的層級,也就是區塊層級蓋過元素層級,元素層級蓋過根層級。

透過 styles 屬性設定樣式,比自己寫 CSS 多了一個實際的好處,使用者能直接在 Appearance(外觀)>Editor(編輯器)>Styles(樣式)介面調整這些值,不需要去改程式碼;而且因為這些樣式最終是由 WordPress 統一輸出,不會遇到傳統 CSS 常有的特異性衝突問題,不用靠 !important 或疊加選擇器去搶優先權。
標題、連結與按鈕等元素樣式,獨立於區塊樣式之外設定
styles.elements 底下可以設定的元素涵蓋連結、按鈕、標題、圖說與引註等。連結對應 <a> 標籤,還能分別設定 :hover、:focus、:focus-visible、:active 這幾種狀態各自的樣式;按鈕對應的是 .wp-element-button 這個 class,同時相容舊版用的 .wp-block-button__link;標題有一個 heading 屬性可以統一設定 h1 到 h6,也可以用 h1 到 h6 各自獨立的鍵名分別覆寫,讓某一級標題有特別的樣式。
每個元素底下能設定的子屬性,結構跟全域樣式是一致的,同樣是 color、typography、spacing 這幾類,不必為了設定元素樣式另外記一套新語法。理解全域樣式怎麼寫,等於同時學會了元素樣式怎麼寫。
覆寫單一區塊的樣式不必新增一行 CSS
呼應前面 settings.blocks 談的「能不能調」,這裡的 styles.blocks 談的是「調成什麼」的實際數值。在 styles.blocks["core/code"] 底下直接寫 color.text、color.background,就能讓 Code 區塊套用專屬的文字色與背景色,其他區塊完全不受影響。
這條路徑跟寫一段傳統 CSS 選擇器(例如 .wp-block-code { color: ...; })能達到的視覺效果一樣,差別在於全程都在 theme.json 裡宣告完成,不需要另外維護一份樣式表,也不會有 CSS 檔案載入順序、特異性優先權這類額外要煩惱的問題。
樣式變化分成全域、顏色、字體與區塊四種檔案,讓使用者一鍵切換風格
樣式變化(style variations)是這幾年才逐漸成熟的機制,本質上是放在主題 /styles 資料夾裡的一組獨立 JSON 檔案,內容結構跟 theme.json 完全一樣,只是平常不會生效,要等使用者在網站編輯器裡主動選取之後才會套用。
目前 Block Editor 支援 4 種樣式變化,各自能覆寫的範圍不一樣:「全域╱主題」可以覆寫 theme.json 裡的任何設定與樣式;「顏色」只能覆寫顏色相關的設定或樣式;「字體」只能覆寫字體相關的設定或樣式;「區塊」只能覆寫特定區塊,以及它巢狀底下的區塊、元素樣式。主題可以同時提供好幾套樣式變化,讓使用者自由組合搭配。
所有樣式變化檔案都要放進主題的 /styles 資料夾(官方建議依類型再分子資料夾,例如 /styles/color、/styles/typography),而且每個檔案都要帶一個 title 屬性,這是使用者在介面上實際看到、用來辨識這套變化的名稱。
顏色與字體樣式變化是後來才新增、可獨立挑選的機制
「顏色」與「字體」這兩種細分的樣式變化,是 WordPress 6.6 才新增的功能。在這之前,主題只能提供「全域」這一種整套替換的樣式變化,使用者想換個顏色組合,得連帶把字體、間距這些設定一起換掉。新機制讓顏色組合跟字體組合可以獨立混搭,使用者能在 Styles 面板裡分別挑選,不必為了換一種顏色搭配,就多做一份完整的全域變化檔。
定義字體樣式變化時,官方文件建議跨所有變化檔案維持一致的 slug 命名慣例,例如都用 primary、secondary 這類語意化名稱,確保同一個 slug 在每個變化檔案裡都對應到正確的字體家族定義,不會因為某個變化檔漏寫而找不到對應的字體。
樣式變化的設定值會存進資料庫,跟子佈景主題直接覆寫檔案不同
子佈景主題(child theme)的 theme.json,運作方式是直接覆蓋父佈景主題的 theme.json 檔案本身。樣式變化不是這樣運作的,使用者一旦在編輯器裡選取某個樣式變化,那份 JSON 的內容就會被當成「使用者自訂」的設定,寫進網站資料庫裡,進而蓋過主題原本 theme.json 裡的設定與樣式。
這個差異會帶來一個容易被忽略的後果,主題作者日後如果更新了某個樣式變化檔案的內容,已經選用過那套變化的使用者不會自動收到這次更新,因為使用者那份資料已經獨立存在資料庫裡了。想拿到新版內容,使用者得自己重新選取一次那套樣式變化,或者先切到別的變化、再切回來,才會重新讀取檔案裡的最新內容。
customTemplates 與 templateParts 兩個欄位,各自登記範本與範本組件的中繼資料
theme.json 頂層還有兩個常被忽略的欄位,作用是登記中繼資料,而不是設定外觀。customTemplates 用來註冊給單篇文章、頁面或自訂文章類型選用的自訂範本,對應的實際範本檔案放在主題的 /templates 資料夾裡;templateParts 用來註冊可以重複使用的範本組件,最常見的例子是頁首與頁尾,對應的檔案放在 /parts 資料夾。
customTemplates 陣列裡的每個物件,接受 3 個屬性:name(對應 /templates 資料夾裡、不含副檔名的檔名)、title(顯示給使用者看、可翻譯的標題)、postTypes(這個範本可以套用在哪些文章類型上,預設是 page)。註冊完成後,使用者在編輯文章或頁面時,範本選單裡就會出現這個有語意的名稱可以挑選。
templateParts 陣列裡的每個物件,接受的是 area(預設可選 header、footer、uncategorized,也可以自訂其他區域)、name(對應 /parts 資料夾裡不含副檔名的檔名)、title。範本組件其實就算不透過 theme.json 註冊也能直接使用,但註冊之後多了幾個實際好處:介面上會顯示可翻譯的標題、在 Site Editor 裡會依區域分類呈現,也比較能跟外掛與樣式變化順利搭配運作。
回頭看最開頭那個常見誤解(theme.json 只是用來設定間距),應該已經站不住腳。它是一份涵蓋色彩、字體、版面、區塊層級覆寫、樣式變化、範本登記的完整設定檔,settings 與 styles 各司其職,custom 開放給主題自訂延伸,customTemplates 與 templateParts 則負責把範本結構講清楚給使用者看。
真正動手寫的時候,不必一次把所有欄位都填滿。多數主題會先從 appearanceTools 打開常用控制、註冊幾組色票與字體開始,再依實際需求慢慢往 styles.blocks、settings.custom 或樣式變化這些進階功能延伸。搞懂這份 JSON 的分工邏輯,比背熟每一個屬性名稱重要得多,遇到沒看過的欄位,也知道該往 settings 還是 styles 底下去找答案。
