用 CSS 變數擴充佈景主題樣式的完整做法

想改佈景主題的某個顏色,結果翻遍 style.css 找到十幾處 #3a86ff,改完一個按鈕沒變、改完按鈕標題又跑掉,這種改一處壞三處的經驗,幾乎每個碰過 WordPress 客製化的人都有。問題不在你不夠細心,而在傳統 CSS 把同一個值散寫在幾十個地方,沒有單一的控制點。

CSS 變數(正式名稱是 CSS 自訂屬性,CSS custom properties)就是用來解決這件事的原生語法。把顏色、間距、字級集中宣告成一個變數,全站要用的地方都指向它,往後要換色只動一行。這篇會從語法講到實際怎麼套進 WordPress 佈景主題:包含怎麼找出主題已經在用哪些變數、怎麼在不改動母主題原始碼的前提下覆寫它,以及區塊主題的 theme.json 把變數玩到什麼程度。

CSS 變數是什麼,跟 Sass 變數差在哪

CSS 變數是瀏覽器原生支援的自訂屬性,用兩個連字號 -- 開頭宣告,再用 var() 函式取值。它最關鍵的特性是「執行時可動態改變」,這點和 Sass、Less 那種預處理器變數完全不同。

Sass 變數在編譯成 CSS 的那一刻就被換成固定值,瀏覽器看到的是已經算好的結果,事後無法再改。CSS 變數則一路活到瀏覽器裡,可以被媒體查詢覆寫、被 JavaScript 改寫、被不同的選擇器層層覆蓋。對佈景主題來說,這代表你能做到「切換深色模式只改幾個變數值,全站連動」這種事,Sass 變數做不到。

基本寫法分兩步。先宣告,再用 var() 取用:

:root {
  --primary-color: #3a86ff;
  --text-color: #333;
  --space-md: 16px;
}

.button {
  background: var(--primary-color);
  padding: var(--space-md);
}

變數名稱區分大小寫,--main--Main 會被當成兩個不同的變數,命名時要一致。

變數宣告在 :root 還是區域選擇器,差在作用範圍

:root 代表文件的根元素,等同於 <html>,把變數宣告在這裡,全站任何元素都取得到,這也是放佈景主題全域色票、間距的標準位置。但變數不是只能宣告在 :root,它遵循 CSS 的層疊與繼承規則,宣告在哪個選擇器,就只有那個元素和它的子元素拿得到。

:root {
  --card-bg: #fff;       /* 全域:所有元素可用 */
}

.dark-section {
  --card-bg: #1a1a1a;    /* 區域:只有這塊及其子元素拿到深色 */
}

.card {
  background: var(--card-bg);
}

同一個 .card 元件,放在一般區塊是白底,放進 .dark-section 裡就自動變深底,因為它讀到的是離自己最近的那個 --card-bg 宣告。這個「就近覆寫」的特性,是後面做主題切換和局部換色的基礎。

實務上有個常踩的坑:變數宣告寫錯選擇器,導致目標元素根本繼承不到。如果你把 --card-bg 宣告在 .card 的兄弟元素而非父層,.card 讀不到就會落回 var() 的後備值或無效。判斷原則很簡單,用變數的元素,必須是宣告變數那個選擇器的本身或後代。

var() 後備值怎麼用,避免變數失效時版面崩掉

var() 接受第二個參數當後備值,當第一個變數無效或根本沒宣告時,就改用後備值。這在覆寫別人的主題時特別重要,因為你不一定確定對方有沒有定義某個變數。

/* 若 --link-color 沒宣告,就用 #06c */
a {
  color: var(--link-color, #06c);
}

後備值也能巢狀,一個變數抓不到就找下一個:

a {
  color: var(--link-color, var(--text-color, #333));
}

要注意後備值只在「變數未定義」時生效,不是在「值算出來不合法」時。如果 --link-color 被宣告成 12px 這種不能當顏色的值,瀏覽器不會退回後備值,而是讓整條 color 宣告失效。所以後備值能擋的是「沒這個變數」,擋不了「變數值填錯型別」。

怎麼找出佈景主題已經在用哪些 CSS 變數

覆寫主題前的第一件事,是先搞清楚它定義了哪些變數,而不是憑空新增。多數現代佈景主題都把色票、字級、間距宣告在 :rootbody,你只要把它們挖出來就能對症下藥。

最直接的做法是用瀏覽器的開發人員工具。在前台頁面按 F12 開啟,選取 <html> 元素,在樣式面板裡找到 :roothtml 的規則區塊,所有 -- 開頭的宣告會列在那裡。把要改的變數名稱抄下來,例如 --wp--preset--color--primary 或主題自訂的 --theme-accent

另一個方式是直接看主題的 style.css 或 theme.json。經典主題的變數通常集中在 style.css 開頭的 :root 區塊;區塊主題則是由 theme.json 自動產生(下一段會講)。先找到變數的真實名稱,覆寫才有對象,盲目新增一個主題沒在讀的變數不會有任何效果。

如何在不改母主題原始碼的前提下覆寫變數

知道變數名稱後,覆寫的核心觀念是「重新宣告同一個變數,用更高的層疊優先序蓋過原值」。重點是不要去改母主題的 style.css,因為主題一更新就被覆蓋掉,所有客製化付諸流水。WordPress 有兩條安全路徑。

第一條是「外觀」選單裡的「附加的 CSS」(Additional CSS)。這裡寫的樣式會在主題樣式之後載入,優先序天然較高,適合改個幾項變數:

:root {
  --wp--preset--color--primary: #d6336c;
  --theme-content-width: 760px;
}

第二條是建立子佈景主題(child theme),把覆寫寫進子主題的 style.css。改動量大、或要長期維護時走這條,因為它和母主題各自獨立,母主題更新不會動到你的覆寫。

如果覆寫沒生效,多半是兩個原因。一是母主題把變數宣告在比 :root 更具體的選擇器(例如 .site-header),你只覆寫 :root 層級壓不過它,得用同樣或更具體的選擇器去蓋。二是變數名稱抄錯,差一個字就無效。遇到不生效,回開發人員工具確認變數名稱和宣告它的選擇器,比硬加 !important 更能根治。

區塊主題的 theme.json 怎麼把變數自動化

如果你用的是區塊主題(block theme,採用全站編輯的那種),多數變數不該手寫 CSS,而是在 theme.json 設定,WordPress 會自動產生對應的 CSS 變數。這是 WordPress 官方目前主推的做法。

在 theme.json 裡定義的色票、字級、間距會被轉成固定命名規則的 CSS 變數。一個 slug 為 primary 的顏色色票,會自動產生 --wp--preset--color--primary;放在 custom 區段的自訂值,則依 --wp--custom--變數名 的規則產生。

{
  "version": 2,
  "settings": {
    "color": {
      "palette": [
        { "slug": "primary", "color": "#3a86ff", "name": "主色" }
      ]
    },
    "custom": {
      "contentWidth": "760px"
    }
  }
}

上面這段會讓你在任何地方都能用 var(--wp--preset--color--primary)var(--wp--custom--content-width)。在 theme.json 內部互相引用時,則用 var:preset|color|primary 這種專屬語法。

theme.json 的好處是同一份設定同時餵給前台、區塊編輯器、和全站編輯介面,編輯者在後台選色票時看到的選項,和前台實際渲染的變數是同一套,不會各寫各的。經典主題沒有 theme.json,仍需在 style.css 手寫 :root 變數,兩種路線取決於你的主題類型。

用變數做深色模式與響應式,一份宣告連動全站

CSS 變數真正發揮威力的場景,是把它跟媒體查詢、class 切換、JavaScript 組合起來,做到「只改變數值、全站連動」。深色模式是最典型的例子。

做法是把所有會隨主題改變的顏色都抽成變數,淺色當預設值,再用一個選擇器整批覆寫成深色。常見的觸發方式有兩種,一是跟著系統偏好,二是讓使用者手動切換:

:root {
  --bg: #fff;
  --text: #333;
}

/* 跟隨系統深色偏好 */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --text: #eee;
  }
}

/* 或由按鈕在 <html> 加上 data-theme="dark" 觸發 */
[data-theme="dark"] {
  --bg: #1a1a1a;
  --text: #eee;
}

body {
  background: var(--bg);
  color: var(--text);
}

整個版面只要讀的是 var(--bg)var(--text),切換時不必逐個元件改樣式,改的只有那幾個變數值。手動切換則靠一小段 JavaScript 在 <html> 上加減 data-theme 屬性,並把選擇存進 localStorage,下次載入頁面時自動套用:

const root = document.documentElement;
// 讀使用者上次的選擇
if (localStorage.getItem('theme') === 'dark') {
  root.setAttribute('data-theme', 'dark');
}
// 切換按鈕
document.querySelector('.theme-toggle').addEventListener('click', () => {
  const isDark = root.getAttribute('data-theme') === 'dark';
  root.setAttribute('data-theme', isDark ? 'light' : 'dark');
  localStorage.setItem('theme', isDark ? 'light' : 'dark');
});

響應式同理。把字級、間距、欄寬設成變數,在媒體查詢裡只改 :root 的變數值,所有用到的地方一起縮放,不用為每個斷點重寫一堆規則。變數也能搭配 calc() 做衍生計算,例如標題字級設成內文的 1.5 倍,內文一改標題跟著動:

:root {
  --font-base: 16px;
}
@media (max-width: 768px) {
  :root { --font-base: 14px; }
}
p  { font-size: var(--font-base); }
h1 { font-size: calc(var(--font-base) * 1.5); }

用 CSS 變數整理佈景主題前,先想清楚這幾件事

CSS 變數對佈景主題的價值,不在語法多炫,而在它把分散的樣式收斂成單一控制點:色票集中在 :root 或 theme.json,全站指向同一個變數,往後換色、做深色模式、調響應式都只動源頭一處,不再滿檔案找 #3a86ff。覆寫別人的主題時,先用開發人員工具挖出真實的變數名稱,再透過「附加的 CSS」或子主題去重新宣告,避免直接改母主題被更新洗掉。

要留意的是相容性與型別。CSS 變數在現行主流瀏覽器都支援,但 IE 完全不認得,若你的讀者還有 IE 用戶,關鍵樣式得搭配後備值;後備值能擋變數未定義,擋不了值填錯型別,這點要分清楚。區塊主題優先用 theme.json 管理變數,經典主題則在子主題 style.css 手寫 :root,先確認自己的主題屬於哪一種,再決定動手的位置。從整理現有色票開始,把最常改的三五個值抽成變數,你會立刻感覺到客製化從「改一處壞三處」變成「改一行全站連動」。

相關文章
標籤: theme.json, WordPress 佈景主題, CSS 變數, 深色模式, 子佈景主題