theme.json 間距設定與留白系統完整設定指南

同一個區塊主題,有人改完版面段落之間的留白整齊一致、長得像精品官網,有人則是段落忽寬忽窄、編輯器裡拉出來的間距還跟前台對不上。差別往往不在 CSS 寫得多漂亮,而在 theme.json 間距設定有沒有先把一整套「間距尺度」定義好。把這層基礎打穩,後面不管是區塊內距、區塊之間的留白,還是讓客戶在編輯器裡自己調,都有一致的依據可循。

這篇會把 theme.json 裡 settings.spacing 的每個欄位拆開講清楚:spacingScale 怎麼自動長出一套尺度、spacingSizes 怎麼手動刻、slug 為什麼用數字、v2 跟 v3 的行為差在哪,以及實際把預設套到區塊上時最容易卡住的地方。看完你應該能替自己的主題設計出一套站台共用的留白系統。

theme.json 間距設定到底管哪些東西

settings.spacing 這個物件管的是跟 margin(外距)、padding(內距)、gap(區塊間距)相關的所有全域設定。它不是直接「套樣式」,而是定義「主題提供哪些間距選項、使用者能不能調、能調哪些單位」。真正把數值套到區塊上是 styles 那一層的事。

settings.spacing 底下有這幾個屬性,先建立全貌:

  • blockGap:是否開啟「區塊間距」這個控制項,值可以是 truefalsenull,預設 null
  • margin:是否在支援的區塊上開啟外距控制項,預設 false
  • padding:是否開啟內距控制項,預設 false
  • customSpacingSize:是否允許使用者自己輸入任意間距數值,預設 true
  • spacingScale:一個設定物件,讓 WordPress 依規則自動產生一整套間距尺度。
  • spacingSizes:一個陣列,用來手動定義每一個間距尺寸,可以覆蓋或補充 spacingScale 產生的結果。
  • units:使用者輸入自訂間距時可以選的 CSS 單位清單,例如 pxemremvhvw%

這裡先抓一個關鍵心智模型:marginpaddingblockGap 這幾個是「開關」,決定使用者在編輯器側欄看不看得到那個控制項;spacingScalespacingSizes 才是「內容」,決定那個控制項裡有哪些可選的間距級距。兩者分工不同,設定時不要混為一談。

spacingScale 怎麼自動產生一整套間距尺度

spacingScale 的用途是讓你用「一條規則」長出一整排間距,不必一個一個手寫。WordPress 預設就帶了一組七階尺度,從中間的基準值往兩側各推三階。

預設設定長這樣:

{
  "version": 2,
  "settings": {
    "spacing": {
      "spacingScale": {
        "operator": "*",
        "increment": 1.5,
        "steps": 7,
        "mediumStep": 1.5,
        "unit": "rem"
      }
    }
  }
}

逐個欄位拆解它在算什麼:

  • mediumStep:整套尺度的中間基準值,這裡是 1.5,也就是 1.5rem。整套尺度會以它為中心往兩邊長。
  • steps:總共要產生幾階,7 代表中間一階加上下各三階。
  • operator:推算下一階用的運算子,* 是相乘,+ 是相加。
  • increment:每往外一階要乘上(或加上)的量,這裡是 1.5
  • unit:整套尺度用的 CSS 單位,這裡是 rem

用乘法 * 搭配 1.5 的倍率,往大的方向會像 1.5 → 2.25 → 3.375 這樣等比放大,往小的方向則等比縮小。這種等比級數的好處是每一階之間的視覺落差感覺一致,比起自己亂填一排數字更協調。如果你想要的是等差(每階固定加同樣的量),就把 operator 換成 +

需要更細或更粗的尺度時,直接調 steps(要幾階)和 increment(每階差多少)即可。想完全停用這套自動尺度,把 spacingScale.steps 設成 0,WordPress 就不會生成任何預設間距。

spacingSizes 怎麼手刻每一個自訂間距

當你的設計需求沒辦法用一條等比或等差規則漂亮地長出來時,就改用 spacingSizes 自己刻。它是一個物件陣列,每個物件代表一個間距尺寸,你完全掌控每一階的數值與名稱。

每個尺寸物件要定義三個屬性:

  • name:給使用者看的可讀名稱,會顯示在編輯器側欄,也可被翻譯成其他語言,例如「小」「中」「大」。
  • slug:這個尺寸的識別代號,會被拿去組成 CSS 自訂屬性。
  • size:實際的 CSS 數值,可以是 1.5rem24px 這類具體值,也可以用 clamp() 做流體間距。

範例:

{
  "version": 2,
  "settings": {
    "spacing": {
      "spacingSizes": [
        { "name": "小", "slug": "20", "size": "0.75rem" },
        { "name": "中", "slug": "50", "size": "1.5rem" },
        { "name": "大", "slug": "80", "size": "4rem" }
      ]
    }
  }
}

要注意 spacingScalespacingSizes 同時存在時的行為,在不同 theme.json 版本下結果不一樣,這點放在後面版本差異那節細講。一個實務原則是:除非你真的要每階都精算,否則先用 spacingScale 把骨架長出來,只在少數需要插隊的位置用 spacingSizes 補幾階,會比整套手刻省事。

為什麼間距 slug 用 20、30 而不是 sm、md、lg

WordPress 自動產生的間距預設,slug 是 20304050607080 這種數字,而不是常見的 T 恤尺碼 smmdlg。這不是隨便取的,背後有三個設計考量,理解它能幫你避開命名上的坑。

第一、數字天生好排序。把間距從最小排到最大,數字一眼就能比大小,T 恤尺碼則沒有這種天然順序。第二、留出插隊空間。如果某天你想在「中」和「大」之間塞一個尺寸,用數字只要取 55 就插進去了,不必把整套重新命名。第三、跨主題的內容相容性。當一個從區塊圖樣庫拿來的圖樣用了某個間距 slug,而你的主題剛好沒支援那個 slug,數字制可以退而求其次對應到最接近的值;這也是為什麼整套尺度是以中間的 50(基準)為核心往兩側生成的,有一個已知的「中」基準 50,這種降級對應才能盡量準確。

換句話說,數字 slug 是為了「可維護」與「可互通」而設計的。自己手刻 spacingSizes 時,建議沿用這套數字慣例,不要改用 smalllarge 這類字串,免得跟核心預設或他人的圖樣對不上。

v2 與 v3 的間距行為差在哪

theme.json 的版本號會在 API 出現破壞性變更時往上跳,舊版本在新版 WordPress 裡仍會持續支援,所以你是「主動選擇」要不要升上去。間距這塊在 v2 跟 v3 之間正好有一個會咬到人的差異,跟「主題自訂間距會不會蓋掉核心預設」有關。

在 v2 裡,行為是這樣:同時設定 spacingSizesspacingScale 時,WordPress 只採用 spacingSizes 的值,spacingScale 會被忽略。而當主題提供的間距 slug 跟核心預設撞名時,主題的一律覆蓋核心的。

到了 v3(WordPress 6.6 起),多了一個 settings.spacing.defaultSpacingSizes 開關,預設值是 true,行為也跟著改:

  • defaultSpacingSizestrue 時,編輯器會顯示核心提供的預設間距(slug 為 2080),同時禁止主題用這些相同 slug 去覆蓋。
  • defaultSpacingSizesfalse 時,會隱藏核心預設間距,主題才能拿這些 slug 來用自己的值。

另外在 v3 裡,spacingScalespacingSizes 不再是二選一,兩邊產生的預設會合併並依 slug 數字排序;若 slug 撞名,spacingSizes 定義的會蓋過 spacingScale 生成的那一階。這跟 v2 的「只取其一」是完全不同的邏輯。

實務上的判斷很單純:如果你的最低支援 WordPress 版本已經到 6.6,且你有自訂間距想沿用核心的數字 slug,就升到 v3 並把 defaultSpacingSizes 設成 false,行為才會貼近你原本在 v2 的預期。傳統(classic)主題則有對應的 default-spacing-sizes theme support,當你已經宣告 editor-spacing-sizes 時它會自動變成 false

blockGap、margin、padding 三個開關怎麼設

這三個是控制「使用者在編輯器看不看得到對應控制項」的開關,跟前面的尺度內容是兩回事。其中 blockGap 最常被搞混,先把它講透。

所謂 block gap(區塊間距),指的是頁面上區塊與區塊之間的留白。在預設的流(flow)版面裡,它是用相鄰元素的 margin-top 來實作的;在 flex 或 grid 版面裡,則是用 CSS 的 gap 屬性實作。WordPress 預設會自動輸出這段 block gap 的 CSS。

blockGap 三個值的差別整理如下:

blockGap 區塊間距控制項 WordPress 自動生成 CSS
null(預設) 不顯示 不生成
true 顯示 生成
false 不顯示 生成

留在預設的 null 通常不建議,因為它連 WordPress 自動生成的區塊間距 CSS 都一併關掉,你得自己寫 CSS 來維持版面的垂直節奏。多數情況你會想要一致的區塊間距,所以把它設成 true,讓使用者能在支援的區塊(例如貼文範本 Post Template)上看到「區塊間距」控制項:

{
  "version": 2,
  "settings": {
    "spacing": {
      "blockGap": true
    }
  }
}

marginpadding 則單純許多,兩者預設都是 false,也就是區塊側欄不顯示外距與內距控制項。想讓使用者能調,就各自設成 true。要不要開放這兩個,取決於你想給編輯者多大的自由度:交給客戶維護的網站,有時反而會把 margin 關掉、只留 blockGap,避免版面被調亂。

把間距預設套用到區塊與留白系統

定義好尺度只是第一步,真正讓留白生效是在 styles 那一層。WordPress 會把每個間距 slug 轉成一個 CSS 自訂屬性,命名規則固定為 --wp--preset--spacing--{slug},例如 slug 是 md 就變成 --wp--preset--spacing--md,slug 是 50 就變成 --wp--preset--spacing--50。理解這個命名規則,你才知道後面怎麼引用。

最常先設的是整站的區塊間距。WordPress 預設的 block gap 大約是 24px,幾乎每個主題都會想改掉它。假設你的設計需要區塊之間預設留 2rem,在 styles.spacing.blockGap 設一個單一值即可(blockGap 只吃單一值,不像 margin、padding 可以分四邊):

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "styles": {
    "spacing": {
      "blockGap": "2rem"
    }
  }
}

要把某個間距預設套到特定區塊上,就用前面那個 CSS 自訂屬性。以下是替核心 Pullquote 區塊加上下內距、邊框寬度也借用間距預設的例子:

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "styles": {
    "blocks": {
      "core/pullquote": {
        "border": {
          "style": "solid",
          "width": "var( --wp--preset--spacing--10 )"
        },
        "spacing": {
          "padding": {
            "top": "var( --wp--preset--spacing--30 )",
            "bottom": "var( --wp--preset--spacing--30 )"
          }
        }
      }
    }
  }
}

這裡有個值得記住的延伸用法:間距預設本質上就是 CSS 自訂屬性,所以它不限於拿來當 margin 或 padding,連邊框寬度都能引用,整個主題需要「一致的尺寸基準」的地方都能共用同一套變數。這正是「留白系統」的核心精神——不是每個區塊各填各的數字,而是大家都引用同一組級距。

設定間距時最常踩的雷與排查

有幾個坑在文件裡藏得比較深,這裡先點名,省得你卡半天:

第一、blockGap 會壓過你給標題加的上方 margin。常見需求是「標題前面多留白、標題跟下一段貼緊一點」,但你在 theme.json 給標題加的 margin-top 幾乎一定會被 blockGap 設定覆蓋掉,這種情境通常只能自己補一段 CSS。例如在 blockGap2rem 的前提下,想讓標題上方有 3rem、緊接標題的下一個元素只留 1rem:

.is-layout-flow * + :is( h1, h2, h3, h4, h5, h6 ),
.wp-block-post-content * + :is( h1, h2, h3, h4, h5, h6 ) {
  margin-top: 3rem;
}
.is-layout-flow :is( h1, h2, h3, h4, h5, h6 ) + *,
.wp-block-post-content :is( h1, h2, h3, h4, h5, h6 ) + * {
  margin-top: 1rem;
}

這段特地把 .wp-block-post-content 也選進來,是因為文章編輯器本身不知道前台的版面長怎樣;只選一邊會造成編輯器跟前台的間距對不上。

第二、編輯器跟前台間距對不上,多半就是上面這類「前台 CSS 沒同步進編輯器」造成的,排查時先確認自訂 CSS 的選擇器有沒有同時涵蓋前台版面類別(如 .is-layout-flow)與編輯器的內容容器。

第三、改了 spacingScale 卻看不到新尺度,先確認 steps 不是 0(那會整套關掉),再確認你用的版本下 spacingSizes 有沒有把它整個取代(v2 行為)或撞名覆蓋(v3 行為)。

把這套尺度先搭好,再決定哪些開關開給編輯者,留白系統就有了一致的地基。先用 spacingScale 長出骨架、必要時用 spacingSizes 補階、依目標 WordPress 版本決定要不要升 v3 並處理 defaultSpacingSizes,最後在 styles--wp--preset--spacing--{slug} 把預設套到區塊上。動手替你正在開發的主題寫下第一組 spacingScale,再回頭微調級距,會比一開始就想把每一階都算到完美來得實際。

相關文章
標籤: 區塊主題, theme.json, spacingScale, spacingSizes, 留白系統