上一篇做完 Interactivity API 我們手上其實已經累積了一整套資產:十幾個區塊、一份設計 Token、一套配色和字級,但它們散在 blocks/ 資料夾和 theme.json 裡,你看不到全貌也不知道品質齊不齊。
今天做兩件事順序很重要:**把整套設計系統攤在一頁上檢視,再拿它們組成完整頁面,**最後介紹 Everything-WP 的 /make-block,它把從「一個參考網站」到「一整套區塊加一頁 UI Library」的流程一次跑完。
直覺上會想先組首頁,畢竟那是看得到成果的東西,但實際做過幾次之後我改成相反的順序,理由很簡單:你不會拿著一盒還沒清點的零件直接開始拼。
組頁面的動作是「挑元件、決定順序、排版」,前提是你知道手上有哪些元件、每個長什麼樣。更重要的是設計上的破綻要在組頁之前修掉,等頁面組好了才發現三張卡片的圓角不一致、間距節奏亂掉,你要回頭改的就不只是區塊,還有已經用了它們的每一頁。
UI Library 就是那張清單,也是那道驗收關卡。

如果你用過 React 生態的 Storybook,概念一模一樣:把每個元件的每種變體(variant)都渲染出來排在一頁當作設計系統的活文件。設計師來對樣式、工程師來查有哪些現成元件、客戶來看整體風格都看這一頁。
對區塊佈景主題來說它還多一層意義,因為我們每個區塊都吃 theme.json 的屬性,把它們全排在一頁時你能立刻看出設計有沒有一致:所有卡片的圓角一樣嗎?間距的節奏對嗎?主色出現的地方協調嗎?分散在各頁時看不出來的破綻,集中在一頁就無所遁形。
我的做法是把 UI Library 做成一個 pattern,放在 patterns/ui-library.php。版面是固定的兩欄:左邊 25% 是一個 sticky 的錨點選單,把區塊分成 Shared、Elements、Sections 三組;右邊 75% 每個區塊一個段落,把它的每種變體都當成真實的 live 實例渲染出來。
左邊那個選單的樣式,就寫在 style.css 裡(這也是 style.css 在區塊佈景主題少數還會寫 CSS 的地方之一):
@media (min-width: 1200px) {
.ui-lib-nav {
position: fixed;
top: var(--wp--preset--spacing--70);
left: var(--wp--preset--spacing--60);
width: 12rem;
background-color: var(--wp--preset--color--base);
border: 1px solid var(--wp--preset--color--border);
border-radius: var(--wp--custom--radius--large);
}
}
UI Library 最容易做壞的地方是把它做成一堆靜態截圖,正確做法是讓每個變體都是「真的那個區塊」,直接把 wp:block-theme/service-card 放進去渲染。這樣它永遠反映區塊的當下樣子,改了區塊這頁自動更新,這也是為什麼要把它做成 pattern,而不是一張設計稿。
實務上我會再加一條規則:每完成一個區塊,就同步把它加進 UI Library,讓這頁隨著開發長大,一個區塊沒進 UI Library 就算沒完成,稽核時會被抓出來。

做到這裡你可能會想到:網站編輯器的「設計 → 樣式 → 區塊」本來就有一份區塊清單,點進去每個區塊都能調顏色、字型、間距,那不就是內建的 UI Library 嗎?
先講一個你八成會遇到的狀況:打開那份清單,自己做的區塊多半一個都不在裡面。原因是至少要有一個樣式面板區塊才會出現在那份清單裡,而面板存不存在,取決於 block.json 的 supports。如果你的區塊只宣告了 html 和 align,這兩個都不產生樣式面板,於是它就被 return null 掉了而不會出現。
要讓它出現,就把樣式類的 supports 開起來。文字型的區塊我會這樣開:
"supports": {
"html": false,
"align": false,
"color": { "text": true, "background": true },
"typography": {
"fontSize": true, "lineHeight": true, "textAlign": true,
"__experimentalFontWeight": true, "__experimentalLetterSpacing": true
},
"spacing": { "margin": true, "padding": true }
}
卡片型的則是 color、spacing、__experimentalBorder、shadow。注意邊框那個鍵名是 __experimentalBorder 不是 border,核心的 group、image、columns 到現在都還這樣寫。另外 render.php 要有 get_block_wrapper_attributes(),面板調出來的值才會真的套到 markup 上。
補完 supports 之後,你的區塊確實都會出現在那份清單裡,但它取代不了 UI Library:
| 樣式 → 區塊清單 | UI Library 頁面 | |
|---|---|---|
| 一次看到 | 包含所有區塊 | 自訂區塊 |
| 看到的內容 | example 的單一預覽 |
真實內容渲染的每個變體 |
| 用途 | 調樣式 | 檢視一致性、驗收 |
| 給客戶或設計師看 | 要進後台逐個點 | 一個網址捲一遍看完 |
| 改動存到哪 | 資料庫(wp_global_styles) |
不改任何東西,純檢視 |
你想比較「三張卡片的圓角一不一致」或是「主色出現的地方協不協調」時,那份清單幫不上忙,它一次只給你看一個區塊,而且看的是 example 那筆假資料。
最後一列的差別更值得留意:樣式面板改出來的東西存在資料庫(wp_global_styles),它會蓋過 theme.json,這就是之前談過的「檔案與資料庫誰是真相來源」,這代表你每開一個樣式 supports,等於多開一道讓人繞過 theme.json 的門。
所以開哪些 supports 是設計決策不是技術問題:**哪些區塊你願意讓網站擁有者微調、哪些要鎖死,**一個參考的分法是最小元件(badge、tag、button)開 color 加 typography,版面型區塊(hero、page-header)只開 spacing,卡片型看客戶要不要能換底色。
檢視完、修掉 UI 問題才輪到組頁面,區塊佈景主題的首頁是 templates/front-page.html,它不是 PHP 而是一份區塊標記,組首頁的動作本質上就是「把區塊依順序疊起來,用容器區塊排好版」:
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:block-theme/hero {"align":"full"} /-->
<!-- wp:group {"align":"full","backgroundColor":"surface","style":{"spacing":{"padding":{"top":"var:preset|spacing|80","bottom":"var:preset|spacing|80"}}}} -->
<div class="wp-block-group alignfull has-surface-background-color has-background">
<!-- wp:block-theme/section-head {"eyebrow":"SERVICES","heading":"服務項目"} /-->
<!-- wp:columns -->
...
<!-- /wp:columns -->
</div>
<!-- /wp:group -->
</main>
<!-- /wp:group -->
拆開來看結構很單純,最上面用 wp:template-part 拉進 header,主體用一個 wp:group 當 main 容器,裡面依序放 hero、一個帶背景色的 group 包住服務項目、再一個個往下疊。每個自訂區塊就是一行 wp:block-theme/xxx,核心的 wp:columns、wp:group 負責分欄和分區。
那個 wp:group 的 padding 寫的是 var:preset|spacing|80,背景色是 surface,它們不是隨手打的 48px 或 #f6f7f8,而是指向 theme.json 裡定義好的間距刻度和顏色。
這就是區塊佈景主題的最大好處,因為每個區塊、每個容器用的都是定義好的樣式值,這個首頁不含任何一個寫死的顏色和尺寸,哪天客戶說「主色換一下」,你改 theme.json 一行整個首頁跟著換,不用進來這份 front-page.html 找二十個地方。
手寫這串巢狀的區塊標記很累,括號、tagName、class 一個對不上就壞,這正是 AI 最擅長的地方。我組首頁時的做法是把「版面結構」用自然語言描述給 Claude Code:
首頁從上到下:header、一個滿版 hero、一區服務項目(section-head 標題加三欄 service-card)、一區「這些情況我幫得上忙」(兩欄 check-item)、最後 CTA。服務區給淺灰背景,每區上下留 80 的間距。
它會把上面那種區塊標記整段生出來,wp:group、wp:columns 的巢狀關係、var:preset 的間距都照 theme.json 的值填好。你做的是「決定版面長怎樣」這個設計層的事,繁瑣的標記交給它。
AI 組頁面很快,但有兩個地方我一定會回頭檢查。
第一,有沒有偷塞 inline style。AI 有時為了「快點達到你要的效果」,會直接寫一段 style="margin-top:37px" 這種寫死的值,一旦出現設計樣式的一致性就破了個洞,看到就要它改回 var:preset 的屬性。
第二,滿版和內容寬度對不對。hero 這種要通欄的用 align:full,內文區塊要收在 contentSize 裡,這兩個混亂的話頁面會忽寬忽窄。
到這裡你已經看過完整流程了:抽取設計元素、寫進 theme.json、一個個做區塊、產出 UI Library、組成頁面。Everything-WP 把這條路做成了一個指令 /make-block,輸入是一個參考網址或幾張設計稿:
/everything-wp:make-block https://example.com
/everything-wp:make-block mockup-home.png mockup-about.png
/everything-wp:make-block https://example.com mockup-home.png # 混合輸入也可以
它會跑完五個階段,中間不需要你確認,直到最後才停下來等你看:
Stage 1|抽取:先讀 robots.txt 和 sitemap.xml 把整站頁面盤點出來,依網址分類成 home/page/archive/single,並把同一個範本的頁面歸成一群,例如多篇 /blog/{slug} 只會挑一篇代表來掃。
這一步很關鍵,掃 7 個代表頁跟掃 1.000 頁的差別,就是這條流程跑不跑得完。接著用 agent-browser 打開每個代表頁,抓 computed style 得到精確的 CSS 值,並依「共用元素 → 頁面區段 → 最小元素」三個層次盤點元件,避免 hero 裡的按鈕被重複抽成兩個元件。
Stage 2|theme.json 與工具鏈:呼叫 /init-theme 把主題骨架、PHPCS、PHPStan、CI、建置腳本立起來,然後把抽到的原始值正規化:顏色分群後依角色命名(base、contrast、primary、accent…)收斂成 6 到 10 個語意色、字級對到刻度、間距推成一套 scale。
Stage 3|生成區塊:盤點到的每個元件生成一個動態區塊(block.json + render.php + edit.js + style.css),硬規則是區塊 CSS 不准出現十六進位色碼和寫死的 px、不准有重複的 markup,每完成一個就同步補進 UI Library。
Stage 4|UI Library 頁面:產生 patterns/ui-library.php,在本機站台建一個草稿頁,把前台和編輯器兩個網址交給你,這是整條流程唯一的人工關卡。
Stage 5|一致性稽核:跑腳本檢查寫死的色碼與 px 數、重複的 markup 結構、盤點清單裡有沒有元件漏做,再跑 composer phpcs && composer phpstan,結果寫進 design/consistency-report.md。
跑完之後 design/ 目錄留下的東西,是我覺得這條流程最有價值的部分:
design/
├── page-inventory.json # 掃了哪些頁、怎麼分群的
├── design-tokens.raw.json # 抽到的原始值
├── token-mapping.md # 原始值 → 語意名稱的對照與理由
├── component-inventory.md # 元件清單、變體、在哪幾頁出現
└── consistency-report.md # 稽核結果
這個系列用的 block-theme 就是這樣生出來的,掃的是我自己的部落格。token-mapping.md 裡會這樣記:
| 原始值(使用次數最高) | 語意名稱 | 出現位置 |
| rgb(255,210,77) / rgb(255,220,115) | `primary` #ffd24d | logo、標題強調、序號、圓點、底線 |
| rgb(245,124,0) | `accent` #f57c00 | 分類標籤、連結、箭頭 |
| rgb(239,239,239) | `border` #efefef | 卡片與分隔線 |
捨棄(雜訊/出現次數低):白色半透明 0.7–0.78 — 深色 hero 上的覆蓋文字;
rgb(160,178,185) — 深色卡片上的灰藍 meta。兩者都可用 base/muted 在深底上表達。
它不只給你結果,還記下「為什麼把這兩個相近的黃色合併成一個」「哪些值被當雜訊丟掉」。這份稽核軌跡的用途是,當你三個月後覺得某個顏色怪怪的,可以回頭查它是從哪個網頁的哪個地方來的。
/make-block 有一條寫死在指令裡的規矩,它叫做「憲法」,白話講就是每個視覺值只有一個來源,頁面只能引用、不准重新定義:
| 視覺項目 | 唯一來源 | 頁面/範本裡禁止 |
|---|---|---|
| 顏色、字級、間距 | theme.json 的屬性 |
寫死的 hex 或 px |
| 元件的 markup 與樣式 | 動態區塊與它的 CSS | 複製 markup、蓋 class |
| 頁面 | 範本(只引用) | inline style、重新定義元件 |
這條規矩帶來的好處在 review 階段最有感,你看著 UI Library 頁面說「主色太暗」,要改的是 theme.json 裡的一個值,不是二十個檔案;說「按鈕的 hover 不對」,改那個區塊的 CSS,所有用到它的地方一起更新。永遠不要用「在別的地方多加一段樣式」來修視覺問題,那等於在單一來源上鑿一個洞。
/make-block 到 UI Library 就停了,後面的頁面組裝是對話式的,因為組頁面牽涉的全是判斷題:
| 你的需求 | 實作方式 |
|---|---|
| 這區塊要列出最新 N 篇文章 | 區塊 render.php 跑 WP_Query,attributes 給 postType、數量、分類篩選 |
| 這段文字或按鈕要能在後台改 | 做成區塊 attributes,每個實例各自編輯 |
| 全站共用的設定(電話、社群連結) | 產一個設定頁,區塊讀 get_option() |
| 文章要多一個欄位並顯示在元件上 | register_post_meta 加編輯器面板,區塊在 render.php 讀 meta |
同樣是「這裡放三篇文章」,可能是寫死的三個區塊、可能是 Query Loop、也可能是區塊自己跑 WP_Query,選哪一種取決於誰要維護、多久改一次、要不要客戶自己能調,這種問題沒有標準答案,AI 猜不出來,只能你告訴它。
走完這一輪你會發現,用區塊佈景主題加 AI 做網站,跟傳統主題「切一個 PSD 成範本」是很不一樣的體驗。你不再是像素級地刻每個區塊,而是在決定用哪些現成的區塊、怎麼編排、要留多少間距,剩下的讓 AI 去組合。
而且這套資產不綁死在這個站。區塊的結構和行為(blocks/ 裡的 block.json、render.php、edit.js)可以整包搬到下一個專案,設計的長相(theme.json 的顏色、字級、間距)換一份就好。同一個 service-card,在 A 站是黃底黑字、在 B 站換成藍底白字,區塊程式碼一行都不用改。UI Library 就是你檢視這套可複用資產的窗口,也是交接給下一個專案時最好的說明書。
頁面組好了、設計系統也齊了。不過這一路我們都在做一個全新的主題,而真實世界裡你手上更可能是一個跑了三年的傳統佈景主題,客戶也不會給你打掉重練的預算。下一篇換個場景,談怎麼把舊主題分三個階段搬進 Block Theme,而且每一步都能單獨上線。
文章目錄:oberonlai.blog/category/2026-ithome