自訂區塊樣式 register_block_style 做樣式選單

編輯在區塊編輯器裡反覆貼同一段 CSS、或乾脆開一個新的自訂區塊,只為了讓某張圖片有圓角、某個按鈕變成外框樣式,這是很多 WordPress 網站長期累積技術債的起點。其實核心區塊本身就支援「樣式切換」,你只要用 register_block_style 把樣式註冊進選單,編輯就能在側邊欄一鍵套用,不必碰任何程式碼。

自訂區塊樣式(block styles)是把一段視覺處理包成可重複使用的選項,掛在現有區塊上。它背後的機制單純到出乎意料:WordPress 只是在區塊的外層元素加上一個 .is-style-{名稱} 的 CSS class,剩下的全靠你寫的 CSS 或 theme.json 設定接手。這篇會把 PHP、theme.json v3、JavaScript 三種註冊路徑講清楚,告訴你各自適合什麼情境,並補上多數教學跳過的兩塊:樣式註冊了卻不出現時怎麼排查,以及如何反向移除核心預設樣式來收斂編輯體驗。

自訂區塊樣式到底是什麼,跟區塊變體有何不同

自訂區塊樣式是替「現有區塊」準備的替代外觀選項,註冊後會出現在區塊側邊欄的「樣式」面板,讓編輯在預設樣式與你的版本之間切換。使用者一次只能套一種樣式,不能疊加。

運作原理只有一句話:被選取的樣式會在區塊外層元素掛上 .is-style-{name} 這個 class。例如你註冊一個叫 hand-drawn 的圖片樣式,套用後 HTML 就會多出 is-style-hand-drawn,接著由你提供的 CSS 針對 .wp-block-image.is-style-hand-drawn 去改外觀。理解這點很重要,因為之後排查問題、寫 CSS 選擇器,全都繞著這個 class 名稱打轉。

WordPress 生態裡有好幾個名字裡都帶「variation」的功能,很容易混淆,先分清楚:

  • 區塊樣式(block style variation):本文主題,替單一區塊加替代外觀,掛 .is-style- class。
  • 區塊變體(block variation):同一個區塊的不同預設組態,例如核心嵌入區塊底下的 YouTube、Twitter 其實是同一個 Embed 區塊的不同變體,差在預設屬性,不是換外觀。
  • 全域樣式變體(global style variation):整站層級的配色與排版方案,放在主題 /styles 資料夾,影響的是全站不是單一區塊。

三者各管各的,名字像但用途完全不同。本文只談第一種。

三種註冊方式怎麼選,先看這張對照表

註冊自訂區塊樣式有三條路:PHP 的 register_block_style()、theme.json v3 的 JSON 檔,以及 JavaScript 的 registerBlockStyle()。沒有哪一條絕對最好,取決於你在做主題還是外掛、要不要讓使用者用全域樣式介面再微調、以及目標 WordPress 版本。

方式 適合情境 需要的版本 使用者可在全域樣式再改
PHP register_block_style() + inline CSS 外掛、或想把樣式邏輯集中在 PHP WordPress 5.0+ 否(純 inline CSS)
PHP + style_data 參數 想用 theme.json 式寫法、又走 PHP WordPress 6.6+
theme.json v3 的 /styles JSON 檔 區塊主題、想全部走標準化設定 WordPress 6.6+
JavaScript registerBlockStyle() 只想動編輯器選單、CSS 另外管 WordPress 5.0+

簡單的判斷邏輯是這樣:如果你在做區塊主題、而且能要求 WordPress 6.6 以上,優先用 theme.json v3 的 JSON 檔,因為它最標準化,使用者還能在「外觀 → 編輯器 → 樣式」裡自行調整。如果你在做外掛、或必須相容舊版 WordPress,就用 PHP 的 register_block_style()inline_style。JavaScript 路徑現在主要用在「只想增減編輯器選單項目」的場景,因為它無法附帶伺服器端 CSS,樣式得另外想辦法載入。

用 register_block_style 在 PHP 裡註冊樣式

PHP 是最多主題作者採用的方式。register_block_style() 接收兩個參數:區塊名稱(含命名空間與 slug,例如 core/image)與一個描述樣式屬性的陣列,回傳布林值表示是否註冊成功。函式必須掛在 init hook 上。

屬性陣列裡 namelabel 是必填,其餘可選:

  • name:樣式的唯一識別字,會拿去組成 is-style-{name} 這個 class。
  • label:給人看的標籤,會顯示在編輯器選單,可翻譯。
  • inline_style:直接寫進去的 CSS,樣式被使用時才輸出。
  • style_handle:指向一個已用 wp_register_style() 註冊好的樣式表 handle。
  • style_data:WordPress 6.6 新增,用 theme.json 式的陣列寫樣式。
  • is_default:設 true 可把這個樣式設為該區塊的預設,預設值是 false

下面這段替圖片區塊加一個「手繪感」樣式,把它寫進主題的 functions.php

add_action( 'init', 'ezwps_register_block_styles' );

function ezwps_register_block_styles() {
    register_block_style( 'core/image', array(
        'name'         => 'hand-drawn',
        'label'        => __( '手繪感', 'ezwps' ),
        'inline_style' => '.wp-block-image.is-style-hand-drawn img {
            border: 2px solid currentColor;
            box-shadow: 0 4px 10px 0 rgba( 0, 0, 0, 0.3 );
            border-radius: 255px 15px 225px 15px/15px 225px 15px 255px;
        }',
    ) );
}

存檔後進編輯器點一張圖片,側邊欄的「樣式」面板就會多出「手繪感」選項。inline_style 的好處是樣式只在這個區塊樣式被用到時才印出來,不會無謂地拖慢沒用到的頁面。但如果你的 CSS 超過幾行,塞在 PHP 字串裡會很難維護,這時改用 style_handle 指向一支獨立樣式表,或改走 theme.json 會清爽很多。

用 style_data 參數改用 theme.json 式寫法

style_data 是 WordPress 6.6 加進來的參數,讓你在 PHP 裡用「類 theme.json」的陣列結構描述樣式,而不必手寫 CSS 字串。它最大的附加價值是:用這個方式註冊的樣式,使用者能在「編輯器 → 樣式 → 區塊」介面裡自行調整,等於把調色權交還給網站經營者。

register_block_style(
    array( 'core/image' ),
    array(
        'name'       => 'orange-border',
        'label'      => __( '橘色外框', 'ezwps' ),
        'style_data' => array(
            'border' => array(
                'color'  => '#f5bc42',
                'style'  => 'solid',
                'width'  => '4px',
                'radius' => '15px',
            ),
        ),
    )
);

注意第一個參數現在也接受陣列,代表你可以一次把同一個樣式註冊給多個區塊,例如同時掛在 core/groupcore/columns 上,這正是區段樣式(section styles)的基礎。

在區塊主題裡用 theme.json v3 的 JSON 檔註冊

對區塊主題開發者來說,最簡潔的做法是在主題的 /styles 資料夾新增一個 JSON 檔。這需要把 theme.json schema 升到 v3,而 v3 只有 WordPress 6.6 以上支援,所以你得把主題的最低版本需求訂在 6.6,或要求使用者裝 Gutenberg 外掛。

假設要給圖片區塊加一個藍色外框,建立 styles/blocks/image-blue-border.json

{
    "$schema": "https://schemas.wp.org/trunk/theme.json",
    "version": 3,
    "title": "藍色外框",
    "slug": "blue-border",
    "blockTypes": [ "core/image" ],
    "styles": {
        "border": {
            "color": "#00f9ff",
            "style": "solid",
            "width": "4px",
            "radius": "15px"
        },
        "shadow": "var(--wp--preset--shadow--natural)"
    }
}

這個檔案裡幾個關鍵欄位,對應的正是 PHP 版的參數:

  • title:等同 PHP 的 label,會顯示給螢幕報讀器,也用來在沒給 slug 時自動推算識別字。
  • slug:等同 PHP 的 name,組成 is-style-{slug} class,可含英數字、連字號、底線。
  • blockTypes:一個陣列,指定這個樣式要套到哪些區塊,可填多個,不再限定單一區塊。

存檔後 WordPress 會自動把這個樣式註冊進區塊樣式登記表,選單立刻出現該選項,前後台都套用,而且能在「外觀 → 編輯器 → 樣式 → 區塊」裡再被使用者調整。把這類檔案集中放在 /styles/blocks 子資料夾,WordPress 會自動往子資料夾找,主題作者實測這樣能讓 functions.php 大幅瘦身。

區段樣式與巢狀元素讓 theme.json 接管更多

WordPress 6.6 還開放了一個更強的能力:替「巢狀在區塊內的元素與子區塊」設樣式。換句話說,一個區塊樣式不再只能改外層那一層,連裡面的連結、標題、段落都能一起設計。這就是常被稱作「區段樣式」的概念,本質仍是區塊樣式,只是把目標放到容器型區塊(Group、Columns、Cover)上,並深入它的內部元素。

實務上你可以在 theme.json 的 styles.blocks.區塊名.variations.樣式名 底下,再往下用 elements.linkblocks.子區塊名 去設巢狀樣式,甚至直接寫一個 css 屬性塞自訂 CSS。這對需要「整段切換配色、又要連內部標題段落一起變」的客戶網站特別好用,等於替特定區段建一套可更新的設計系統,改版時只動 theme.json 一處。

用 JavaScript 註冊與移除樣式,以及那個惱人的競態問題

JavaScript 路徑用的是 wp.blocks.registerBlockStyle(),語法和 PHP 版幾乎一樣,差在它只動編輯器選單、不帶伺服器端 CSS。要用它得先在 enqueue_block_editor_assets hook 上掛一支腳本:

add_action( 'enqueue_block_editor_assets', 'ezwps_block_editor_assets' );

function ezwps_block_editor_assets() {
    wp_enqueue_script(
        'ezwps-block-editor',
        get_theme_file_uri( 'assets/js/block-editor.js' ),
        array( 'wp-blocks', 'wp-dom-ready', 'wp-edit-post' )
    );
}

接著在 block-editor.js 裡註冊:

wp.blocks.registerBlockStyle( 'core/quote', {
    name: 'fancy-quote',
    label: 'Fancy Quote',
} );

由於 JavaScript 不負責輸出樣式,對應的 CSS 你得另外用區塊樣式表或主題樣式載入,針對 .is-style-fancy-quote 去寫。

移除樣式有個容易踩的坑,值得單獨講。要拿掉一個樣式用 wp.blocks.unregisterBlockStyle(),但問題在於「先註冊還是先反註冊」會有競態:如果你的反註冊程式碼比註冊程式碼早跑,等於沒作用。正解是把反註冊包進 wp.domReady(),並在 enqueue 時把 wp-edit-post 列為相依,確保你的程式碼在樣式註冊完、DOM 載入後才執行:

wp.domReady( function () {
    wp.blocks.unregisterBlockStyle( 'core/quote', 'large' );
} );

這裡有一條鐵則:樣式用哪種語言註冊,就只能用哪種語言反註冊。PHP 註冊的樣式用 unregister_block_style() 移除,且要把 action 的優先序設高一點(例如 99)讓它在預設優先序 10 的註冊函式之後才跑;JavaScript 註冊的就得用 JavaScript 移除。兩者不能互換。

樣式註冊了卻不出現,照這幾點排查

註冊程式寫對了、選單卻沒反應,是上手階段最常見的卡關,多數教學偏偏跳過。遇到時依序檢查下面幾項,通常逃不出這幾種原因。

  • 區塊名稱打錯:第一個參數必須是完整名稱含命名空間,例如 core/image 不是 imagecore/quote 不是 quote。少了 core/ 樣式會掛不上去也不報錯。
  • 沒掛在 init hook 上register_block_style() 一定要在 init 才呼叫,太早呼叫時區塊登記表還沒準備好。
  • 快取沒清:站台若有頁面快取或物件快取,前台可能還在吃舊版。編輯器端則清瀏覽器快取、重整一次。ezwps 這類有裝快取外掛的站,改完務必把快取整批清掉再驗證。
  • CSS 選擇器特定度不夠:樣式選單出現、卻看不出外觀變化,多半是你的 .is-style-xxx 規則被核心或主題的既有 CSS 蓋過。先用瀏覽器開發者工具看 class 有沒有正確掛上,再視情況提高選擇器特定度,謹慎使用 !important
  • 用 theme.json v3 卻沒升 schema:JSON 檔開頭的 "version" 要是 3,且站台需 WordPress 6.6 以上,版本不符時整個 /styles 檔會被忽略。
  • 古典主題與區塊主題的差異:theme.json 的全域樣式介面調整能力,在古典主題上支援有限,這條路徑以區塊主題為前提。

把這幾點掃過一輪,九成的「樣式不出現」都能定位。剩下的少數情況,多半是和其他外掛搶註冊同名樣式,這時換一個夠獨特的 name 就能避開。

移除核心預設樣式,收斂編輯者的選擇

有時問題不是樣式太少,而是太多。核心區塊自帶的某些樣式(例如引言區塊的某些變體)若不符合品牌規範,留著只會讓編輯誤用。移除的原則前面提過:用什麼語言註冊就用什麼語言移除。

核心區塊的預設樣式多半是伺服器端註冊的,理論上用 unregister_block_style() 即可,但實務上要留意有些核心樣式是在客戶端(JavaScript)註冊的,那就得改用 unregisterBlockStyle()wp.domReady() 移除。判斷不出來時,兩種都試一次最快。移除後重整編輯器,原本的選項就會從面板消失,編輯能挑的範圍變得乾淨可控。

對需要維持視覺一致性的商業網站來說,這一步常被低估。與其事後一頁頁糾正編輯亂套的樣式,不如一開始就把不該用的選項拿掉,只留下符合品牌的幾個自訂樣式,從源頭降低出錯空間。

從一鍵套用到全站一致,自訂區塊樣式該怎麼落地

自訂區塊樣式的價值,在於把「設計決策」從每篇文章手動貼 CSS,收斂成一份可重複套用、可集中維護的選單。決定走哪條路前,先問自己三個問題:你在做主題還是外掛、能不能要求 WordPress 6.6 以上、要不要讓網站經營者在全域樣式介面再微調。答案會直接把你導向 theme.json v3、register_block_style()style_data,或最傳統的 PHP 加 inline_style

如果你正在整理一個 WooCommerce 商店的版面,會更有感:把品牌的按鈕外框、提示框、商品卡片邊框各做成一個區塊樣式,編輯上架商品時直接套,不必每次重調,全站風格也不會走樣。動手時記住那條 .is-style-{name} 的主軸,註冊完先用開發者工具確認 class 有沒有掛上、再回頭看 CSS 特定度,遇到選單沒反應就照上面那份排查清單跑一遍。先用核心圖片或引言區塊做一個最小範例驗證流程,再擴大到整套品牌樣式,是最穩的起手式。

相關文章
標籤: theme.json, 區塊編輯器, WordPress 主題開發, register_block_style, block styles