Wordpress

WordPress 建立子佈景主題教學:4 步驟+除錯攻略

多數人第一次想修改 WordPress 主題,都是直接打開後台的檔案編輯器,找到 style.css 或 functions.php,改一改看看效果。這樣做短期內確實有效,畫面馬上會照你要的樣子跑,直到主題作者推出下一次更新,網站被整包新檔案覆蓋,你熬夜調的顏色、字體、版面全部消失,一行都不留。

問題不在於 WordPress 不穩定,而在於改錯了地方。父主題(Parent Theme)的檔案,本來就是設計給整批覆蓋更新用的,直接在上面塗改,等於在一份會定期被換掉的正本上寫字。真正該做的是先建一個「子佈景主題(Child Theme)」,它就像疊在正本上的一張透明描圖紙——你在描圖紙上寫字、畫線,正本(父主題)不管重印幾次、換幾個版本,描圖紙上的內容都還在,兩張疊起來看,又是完整的一份。WordPress 官方 Theme Handbook 也建議這麼做,因為子佈景主題能讓你不直接改動既有主題的程式碼,還是可以繼續拿到父主題的更新,不會弄丟客製化。

接下來這篇 WordPress 建立子佈景主題教學,會照實際動手的順序走一遍:先建資料夾、寫 style.css 檔頭、用 functions.php 把父主題樣式引進來、啟用測試,最後附上最常見的幾種錯誤該怎麼排解。跟著做完一次,你的自訂修改就有了自己的描圖紙,不用再賭下一次更新會不會把心血覆蓋掉。先從直接改主題檔案為什麼風險這麼高講起。

建立 WordPress 子佈景主題的四個步驟:建立父主題名-child 資料夾、寫 style.css 檔頭、用 functions.php 的 wp_enqueue_style 接上父主題樣式、啟用並驗收前台不變
整個流程只有四步:建資料夾、補上 style.css 與 functions.php 兩個檔案,最後在後台啟用並驗收前台外觀沒跑掉。

直接修改主題檔案,為什麼風險這麼高?

WordPress 每個主題,不管是免費裝的還是花錢買的,都是一整包檔案:style.css 控制外觀、functions.php 掛載各種功能、範本檔案決定頁面怎麼排版。主題作者定期釋出更新,通常是為了修補安全漏洞、跟上 WordPress 核心版本、或補新功能,更新的方式很直接,WordPress 會把整包新版檔案覆蓋掉舊版,資料夾裡原本的內容,不管有沒有被你改過,一律被新檔案取代。

這代表如果你直接打開父主題的 style.css 加了幾行自訂樣式,或在 functions.php 塞了一段自己寫的程式碼,下次更新一按下去,這些修改會跟著舊檔案一起被覆蓋,沒有備份就完全找不回來。子佈景主題解決的正是這個問題。它是一個獨立的資料夾,跟父主題分開存放,更新父主題時 WordPress 只會動父主題的檔案,子佈景主題完全不受影響,客製化自然保留下來。

不過子佈景主題不是萬用工具,用之前要先確認情境對不對。它處理的是外觀與樣式層級的客製化,而且前提是你用的是第三方主題,也就是別人寫的、會持續推送更新的那種。如果你自己從零開發一個主題,本來就不會拿去更新成別人釋出的新版,那其實直接改就好,不必疊一層子佈景主題增加維護的複雜度。另外,子佈景主題也不是唯一的解法:如果你要加的是功能性的東西,像新增自訂文章類型、串接金流、寫一段跟外觀無關的邏輯,更適合的做法是寫成一支外掛,而不是塞進子佈景主題的 functions.php。外掛跟主題脫鉤,以後就算換主題,這些功能也不會跟著不見。

同一次主題更新,直接改父主題檔案會讓客製化被整包新版覆蓋而消失;改寫在子佈景主題資料夾裡則只更新父主題、客製化完整保留
直接改父主題,更新一來客製化就被覆蓋;把客製化放進子佈景主題,更新只動父主題、你的心血照樣留著。

Step 1:在 wp-content/themes 建立子佈景主題的資料夾

決定要建子佈景主題之後,第一步是動手建資料夾,這步不需要寫任何程式碼,只要能連進網站的檔案系統就行,用 FTP 軟體、主機商後台的檔案管理員,或本機架設的開發環境都可以。

連進去之後,找到 wp-content/themes 這個資料夾,裡面每一個子資料夾,對應的就是一套已安裝在這個網站上的主題。動手之前,先確認目前啟用中的主題,它的資料夾名稱是什麼,這個名稱下一步寫 style.css 檔頭時會直接用到,先記下來比較保險。要確認資料夾名稱,可以到後台「外觀 > 佈景主題」比對正在使用的是哪一套,也可以直接展開 wp-content/themes,對照主題預覽圖或資料夾建立時間。

確認好之後,在 wp-content/themes 底下新建一個資料夾,這個資料夾就是子佈景主題本體。命名沒有強制規則,WordPress 不會檢查資料夾叫什麼,但業界慣例是用「父主題資料夾名稱 + -child」,例如父主題資料夾叫 twentytwentyfive,子主題資料夾就取名 twentytwentyfive-child。照這個慣例命名,之後不管是自己回頭維護,還是交接給別人接手,一看資料夾名稱就知道這是哪套主題的子主題,不用另外做筆記。

子佈景主題資料夾 twentytwentyfive-child 和父主題並排放在 wp-content/themes 底下,裡面放 style.css 與 functions.php 兩個檔案
子主題是 wp-content/themes 底下的獨立資料夾,命名慣例為父主題名加上 -child,裡面放 style.css 和 functions.php。

最後一個提醒,整套子佈景主題流程,建議先在測試站或本機開發環境操作一遍,確認建立、啟用、樣式都沒問題之後,再把整包資料夾搬到正式站。直接在正式站上從零開始試,一旦某個環節出錯,訪客看到的就是跑版的網站。

Step 2:寫 style.css 檔頭,用 Template 這一行接上父主題

子佈景主題只有一個檔案是絕對必要的,就是 style.css。在剛剛建好的資料夾裡新增這個檔案,打開它,最上方要用 CSS 註解的格式寫一段檔頭。這段檔頭不是給瀏覽器讀的樣式規則,而是 WordPress 用來辨識「這是誰的子主題」的資訊。

檔頭裡有兩個欄位是必填:Theme Name(子主題在後台顯示的名稱,自己取)、Template(父主題的資料夾名稱)。其餘像 Description、Author、Version、License 都是選填,但建議一併填上,不管是自己過一陣子回頭看,還是交給別人接手,都比較看得懂這套主題的來歷。

這裡藏著全篇最容易出錯的一個環節。WordPress 官方 Theme Handbook 對 Template 欄位的規定很嚴格,它的值必須跟父主題的資料夾名稱完全一致,一個字元都不能差,大小寫也要對得起來。多一個字元、少一個字元,或是大小寫寫錯,WordPress 都沒辦法辨識出這是誰的子主題,子佈景主題輕則不會出現在後台清單裡,重則啟用後直接失效。

一份完整的 style.css 檔頭範例大致長這樣(以父主題資料夾名稱是 twentytwentyfive 為例):

/*
Theme Name: Twenty Twenty-Five Child
Theme URI: https://example.com/twentytwentyfive-child/
Description: Twenty Twenty-Five 的子佈景主題
Author: 你的名字
Author URI: https://example.com
Template: twentytwentyfive
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: twentytwentyfive-child
*/Code language: CSS (css)

把 Template 那一行換成你自己父主題的資料夾名稱,其他欄位可以照自己的習慣調整。這份檔案存好之後,子佈景主題就已經滿足 WordPress 辨識它的最低條件,下一步要把父主題的樣式接進來,不然啟用之後畫面會完全沒有樣式。

Step 3:在 functions.php 用 wp_enqueue_style 把父主題樣式引進來

style.css 檔頭寫好之後,子佈景主題會出現在後台的佈景主題清單裡,但如果現在就啟用,網站會變成一個完全沒有樣式的裸版面,因為 WordPress 不會自動幫子主題載入父主題的 CSS,這件事要自己接。

做法是在子主題資料夾裡新增 functions.php,用 wp_enqueue_style() 這個函式,搭配 wp_enqueue_scripts 這個 action hook,把父主題的 style.css 引進來。這個 hook 雖然名字裡有 scripts,聽起來只跟 JavaScript 有關,但實際上樣式表也是掛在同一個 hook 上處理,這是 WordPress 官方 Theme Handbook 建議的標準做法。

有些比較舊的教學,會教你在子主題的 style.css 裡用 @import 語法引入父主題樣式,這個寫法現在不建議用。@import 是在瀏覽器解析到那一行時才另外發一次請求去抓父主題的 CSS,等於讓樣式表變成前後依序載入,會拖慢頁面顯示的速度;用 enqueue 的方式,兩份樣式表會被 WordPress 一次排好載入順序,效率好上不少。

如果子佈景主題自己的 style.css 裡也寫了自訂樣式(這是常見情況),要記得把子主題的樣式表也一起 enqueue 進去,而且要把父主題的樣式設成子主題樣式的相依項(dependency)。這一步的作用是確保載入順序永遠是「父主題先、子主題後」,子主題的 CSS 規則才蓋得過父主題,你寫的自訂樣式才會真的生效。完整的程式碼大致如下:

<?php
function my_child_theme_enqueue_styles() {
    $parent_style = 'parent-style';

    wp_enqueue_style(
        $parent_style,
        get_template_directory_uri() . '/style.css'
    );

    wp_enqueue_style(
        'child-style',
        get_stylesheet_directory_uri() . '/style.css',
        array( $parent_style ),
        wp_get_theme()->get( 'Version' )
    );
}
add_action( 'wp_enqueue_scripts', 'my_child_theme_enqueue_styles' );Code language: PHP (php)

這段程式碼裡,get_template_directory_uri() 抓的是父主題的路徑,get_stylesheet_directory_uri() 抓的是子主題的路徑,兩個函式名稱很像,容易搞混,寫的時候可以多看一眼確認沒有寫反。array( $parent_style ) 這個相依陣列,就是前面說的「確保父主題先載入」的關鍵那一行。

父主題本身沒有輸出樣式表時,子佈景主題的 CSS 要怎麼載入?

不是每個父主題都會像上面範例那樣,自己去 enqueue 一份 style.css,尤其是新一代的區塊佈景主題,樣式規則大多改用 theme.json 處理,不少父主題根本沒有輸出獨立的 CSS 檔案。這種情況下,就算照 Step 3 的標準寫法,把父主題的 style.css 掛進 wp_enqueue_style(),也不會有任何實際效果,因為父主題本來就沒有這個檔案可以載入。

遇到這種父主題,該做的是改成只載入子佈景主題自己的 style.css,不要再去抓一個父主題根本沒輸出的檔案。做法是把 wp_enqueue_style() 裡抓路徑的函式,換成 get_stylesheet_uri(),這個函式直接指向目前啟用中的樣式表(也就是子主題的 style.css),不依賴父主題有沒有自己輸出樣式。

如果不確定手上這套父主題有沒有輸出樣式表,可以直接打開父主題的 functions.php 或 theme.json 看它怎麼處理樣式;比較省事的做法,是先照 Step 3 的標準寫法測試一次,如果啟用後畫面完全沒吃到樣式,再改用 get_stylesheet_uri() 這個寫法。

Step 4:啟用子佈景主題,確認畫面沒有跑版

functions.php 寫好之後,回到後台「外觀 > 佈景主題」,應該就能看到剛剛建立的子佈景主題出現在清單裡。點下啟用,前台重新整理一次。

後台外觀佈景主題頁面出現剛建立的子佈景主題卡片,點卡片右下角的啟用按鈕就能切換到這個子主題
style.css 與 functions.php 就緒後,子佈景主題會出現在「外觀 › 佈景主題」清單,點右下角的「啟用」就能套用。

這一步驗收的基準很明確,啟用後網站前台應該要「長得跟父主題一模一樣」,一個像素都不差。乍聽有點反直覺,花了力氣建一個新主題,結果畫面卻毫無變化,但這正是應該有的結果,因為現在子主題還沒加上任何自訂 CSS,只是把父主題的樣式原封不動接了過來而已。

正因為這一步的結果應該什麼都沒變,它才適合拿來當中繼站的驗收點。如果啟用後畫面跑版、變成一個沒有樣式的陽春版面,代表 Step 2 或 Step 3 某個環節出了問題,多半是 Template 值寫錯(Step 2),或是 enqueue 沒接對(Step 3)。這時候要回頭逐一檢查,不建議跳過這一步直接往下寫自訂樣式,不然等到畫面亂了,反而搞不清楚問題是子主題本身沒接好,還是自訂樣式寫錯。

還有一個選填的小技巧,可以把父主題資料夾裡的 screenshot.png(或 screenshot.jpg)複製一份到子主題資料夾裡,這樣後台佈景主題清單顯示的縮圖,就會是父主題原本的畫面,方便在一堆主題裡辨識。這步不影響任何功能,單純是為了好認。跟前面每一步一樣,這整套流程還是建議先在測試站或本機環境跑過一輪,確認沒問題,再套用到正式站。

區塊佈景主題(FSE)的子佈景主題,建法有什麼不同?

前面 Step 1 到 Step 4 講的是經典佈景主題(Classic Theme)的做法,如果你現在用的是區塊佈景主題(Block Theme,也就是支援完整站台編輯 Full Site Editing、簡稱 FSE 的那種),建子主題的方式會簡單一些,差異值得先弄清楚,免得套錯流程白忙一場。

第一個差異是子主題的 theme.json 會自動跟父主題合併繼承,不用另外寫程式碼串接。這裡要先澄清一個常見誤解,不管父主題是經典主題還是區塊佈景主題,子主題的辨識機制其實是同一套,都得靠 style.css 檔頭裡的 Template 值讓 WordPress 比對出父主題是誰,並不是「資料夾裡放了 theme.json 跟 style.css 就自動變成子主題」。真正變輕鬆的是樣式設定的繼承,只要子主題資料夾裡也放一份 theme.json(哪怕內容只有基本骨架),WordPress 從 5.9 版開始,就會把子主題 theme.json 裡設定的部分自動合併進父主題的 theme.json,沒特別覆寫的設定值照樣沿用父主題,不用像經典主題那樣自己再寫程式碼去接。

第二個差異是 style.css 的角色變了,但地位沒有降低。即使這份 style.css 裡完全沒有寫任何一行 CSS 規則,它還是不能省略,因為 WordPress 就是靠這份檔案的檔頭(Theme Name、Template)來辨識這是誰的子主題,拿掉它,WordPress 根本不會把這個資料夾當成子主題看待——這點經典主題、區塊佈景主題完全一樣。

第三個差異是樣式改動的優先工具換了。經典主題遇到要調樣式,第一直覺是寫 CSS;區塊佈景主題的第一順位工具變成子主題自己的 theme.json,顏色、字體、間距這些設計代碼(design token)都改在這裡覆寫,只有 theme.json 處理不到的情境(例如 :hover 這類虛擬類選擇器效果)才需要額外寫 CSS。也因為樣式改動的主力搬到 theme.json,functions.php 在區塊主題裡變成非必要檔案,只有想額外載入自訂的 style.css 內容,或要用 PHP 註冊區塊樣式變化(block style variation)這類進階功能時,才需要加上它。

順帶一提,如果父主題完全沒有輸出自己的 style.css(這在純區塊主題身上很常見),要載入子主題自訂的 CSS,一樣得靠 wp_enqueue_style() 搭配 get_stylesheet_uri(),跟前面 Step 3 補充的情境是同一個道理,不會因為換成區塊主題就有例外。

子佈景主題建好卻沒有作用,問題通常出在哪?

照著 Step 1 到 Step 4 一步步做完,大多數狀況都會順利。但如果做完之後畫面不對、或子主題根本沒出現,與其從頭重新查一次資料,不如直接對照下面幾種最常見的狀況,通常一次就能找到卡在哪裡。

後台佈景主題列表找不到剛建立的子佈景主題

最常見的原因,是 style.css 檔頭裡的 Template 值,跟父主題的資料夾名稱沒有一字不差對上,多打一個空格、少一個字元、大小寫不同,都算沒對上。第二個常見原因是檔案根本沒放對位置,style.css 要直接放在 wp-content/themes/你的子主題資料夾/style.css 這一層,不能包在更深一層的子資料夾裡。第三個原因是檔名打錯,WordPress 只認 style.css 這個檔名,如果不小心存成 stylesheet.css 或其他名字,系統完全不會讀取。

排解方法很直接,回頭把 Template 值跟父主題資料夾名稱逐字核對一次,連大小寫都要對,再確認檔案路徑跟檔名完全正確。

啟用後網站樣式跑掉,看起來像沒套用 CSS

這對應的通常是 Step 3 沒接好,常見原因不出三種。第一種是 functions.php 裡寫了 wp_enqueue_style() 這個函式,卻漏掉外面的 add_action() 那一行,沒有掛上 wp_enqueue_scripts 這個 hook,函式就永遠不會被執行;第二種是 add_action() 裡引用的函式名稱,跟實際定義的函式名稱打錯字對不上;第三種是 wp_enqueue_style() 裡抓路徑的函式用錯,把 get_template_directory_uri()(抓父主題路徑)跟 get_stylesheet_directory_uri()(抓子主題路徑)搞混,導致抓錯資料夾。

排解方法是把 functions.php 裡的 enqueue 那段程式碼,逐行跟 Step 3 的範例逐字比對,通常錯的就是這三個地方之一。

CSS 明明改了,前台畫面卻沒有變化

改了 CSS 卻看不到效果,多半不是程式碼寫錯,而是快取或優先度的問題。第一種可能是瀏覽器快取沒清乾淨,或網站本身裝了快取外掛沒清快取,畫面看到的其實是舊版本;第二種可能是父主題的 CSS 選擇器寫得比子主題更精確,優先度比較高,子主題新寫的樣式規則因此被蓋掉,沒有真的生效。

排解方法是先把瀏覽器快取跟網站快取外掛的快取都清掉,用無痕視窗再看一次;如果清完快取問題還在,就檢查子主題那條 CSS 選擇器夠不夠精確,是不是被父主題更精確的選擇器蓋過去了。真的沒辦法透過改選擇器解決,可以在該行樣式的分號前加上 !important 強制蓋過父主題,但這算是排除優先度問題的最後手段,不是常態做法,濫用 !important 會讓之後每一次要再蓋過它的樣式都得跟著加,CSS 只會越改越難維護。

更新父主題後,子佈景主題出現 Fatal error

這個狀況通常來自一個常見的誤會,以為子主題要「繼承」父主題的功能,就把父主題 functions.php 裡的整段程式碼複製貼到子主題的 functions.php 裡。結果是同一個函式名稱,在父主題跟子主題各自的 functions.php 裡都被定義了一次,PHP 沒辦法接受同名函式被定義兩次,會直接噴出 Fatal error,網站白屏。WordPress 官方 Theme Handbook 對這點有明確警告,提醒不要直接把父主題 functions.php 裡的程式碼複製到子主題,這麼做很可能因為函式名稱重複而導致 Fatal error。

正確的觀念是,子主題的 functions.php 不會「取代」父主題的 functions.php,兩份檔案都會被讀取,而且子主題的 functions.php 會先跑。也因為兩份都會執行,子主題的 functions.php 只需要放你要新增的功能,或是要覆寫的那幾個函式,不能整段複製父主題現有的程式碼進去。

排解方法是打開子主題的 functions.php,檢查裡面有沒有函式名稱跟父主題重複,把不必要的複製貼上內容刪掉。如果目的真的是想覆寫父主題某個函式的行為,下一節會講三種正規的做法。

子佈景主題上手後,還能繼續怎麼客製化?

基本的四個步驟做完、確認畫面正常之後,子佈景主題其實還能做的事不只調樣式。這裡先點出三個方向,讓你知道接下來可以往哪裡走,細節不在這篇展開。

第一個方向是覆寫範本檔案。把父主題裡想改的範本檔案,例如 single.php、page.php,複製一份到子主題裡完全相同的路徑,WordPress 靠一套叫做「範本階層(Template Hierarchy)」的機制,渲染頁面時會優先讀子主題裡的那一份。這樣一來,你改的是複製過去的那份檔案,完全不會動到父主題的原始碼,以後父主題更新,你的版面客製化也不受影響。

第二個方向是在 functions.php 裡擴充或覆寫父主題的函式,但不是像前一節那樣整段複製(會撞名出錯)。正規的做法有三種:一種是寫一個跟父主題函式同名的 pluggable function 去覆蓋它(前提是父主題那個函式本身有用 pluggable 的寫法宣告,允許被覆寫);一種是用 remove_action()remove_filter() 先把父主題掛上去的那個函式解除掛勾,再用你自己的函式重新掛上;還有一種是讓自己的函式掛在同一個 hook 上,但用更高的 priority 數字,確保它排在父主題函式之後執行。這三種做法都不會動到父主題原本的檔案,出問題時也比較好回溯。

第三個方向,是如果不想從頭手寫這幾個檔案,部分主題作者會內建子主題產生器,或是可以裝子主題產生外掛,跳過手動建資料夾、寫檔頭的步驟。不過還是建議先手動走過一遍上面的 Step 1 到 Step 4,搞懂每個檔案在做什麼,之後不管用不用產生器,真的出問題時才知道該從哪裡查起。

回到最開頭那張描圖紙的畫面,子佈景主題做的事,就是讓你放心在描圖紙上塗改,正本不管重印幾次、換過幾個版本,描圖紙上的內容都還在,兩張疊在一起看,又是完整的一份。

與其等下一次主題更新、辛苦調好的客製化被整包覆蓋掉才想到要建子主題,不如現在就照 Step 1 開一個資料夾動手。這件事成本其實很低,多一個資料夾、一份 style.css、一份 functions.php,花不了多少時間,卻能讓你在下一次主題更新推播過來的時候,不用再重做一次。

資料來源
  1. Child Themes – Theme Handbook — WordPress 官方 Theme Handbook