一、沒有這做法時踩過的坑
兩年前替零售客戶做後台,元件庫放在獨立的 Storybook 站台。上線前兩週設計師與工程師看過都沒問題,客戶驗收卻說「跟你們給我看的不一樣」。原因是 Storybook 有自己一份精簡 CSS 入口,沒載入專案新增的表單重置樣式。結果:
- story 比實際 Blade 元件少了 4 個變體,沒人補。
- 設計師照 Storybook 標尺寸、工程師照專案改,來回三天。
- 客戶在 staging 看到第三種樣子。
不是輸給工具,是輸給「多一份要同步的東西」。
二、我們的做法(What)
一句話:元件庫不是另一個站,而是專案本身的一條路由。
每個 Laravel 專案都有一條 /_ui 路由,用與正式頁面相同的 layout、Tailwind 產物與 Blade 元件,把所有元件狀態渲染成一頁,三方開同一 URL。
流程:
Figma Variables(唯一來源) → tokens.json(單向不回寫) → Tailwind @theme 變數 → Blade 元件只吃 Tailwind class → /_ui 渲染真實元件 → Playwright 截圖比對 → GitHub Actions 擋 PR → 客戶用同一個 URL 驗收
關鍵是:鏈上沒有「為展示而存在的副本」。/_ui 壞掉就代表正式頁面壞掉。
三、為什麼這樣做(Why)
Storybook 本身沒問題。它解決「元件在應用程式之外獨立開發」,對有專職前端、元件跨多產品共用的組織,它的官方文件說得很清楚。
但我們是三到六人的接案團隊、每案一套獨立 Laravel 應用、元件幾乎不跨案共用。取捨是:
- 放棄隔離開發,換零漂移。真實 layout 較易被外層樣式干擾,但不會「展示對、正式錯」。
- 放棄自動生成的 props 文件,換可點的真實狀態。客戶要看的是 disabled 按鈕與驗證失敗長什麼樣。
- 放棄雙向同步,改嚴格單向。Figma 是顏色、間距、字級的唯一來源,程式端只能消費;雙向同步要有人值班。
四、具體怎麼做(How)
Step 1:在 Figma Variables 建 token collection,顏色、間距、圓角、字級各一組,命名對齊 Tailwind namespace。
Step 2:用 Figma REST API 的 variables endpoint 匯出,存進 repo。
design/tokens.json
resources/css/app.css
resources/views/components/ # Blade 元件本體
resources/views/_ui/index.blade.php # 樣板頁
tests/visual/ui-gallery.spec.ts
{
"color": {
"brand": { "500": { "value": "#F59E0B" }, "600": { "value": "#D97706" } }
},
"radius": { "card": { "value": "12px" } },
"space": { "gutter": { "value": "24px" } }
}
Step 3:用 Style Dictionary 編成 CSS 變數,餵進 Tailwind 的 @theme,token 直接變成 utility class。
@import "tailwindcss";
@theme {
--color-brand-500: #F59E0B;
--radius-card: 12px;
}
Step 4:寫 /_ui 樣板頁,套用與正式頁面同一份 layouts/app.blade.php,列出每個元件的所有狀態;路由只在非 production 或簽章連結開放。
Step 5:用 Playwright 的 toHaveScreenshot() 做視覺回歸,首次執行產生基準圖;同一頁順便跑 axe 掃描,對比度對齊 WCAG 2.2 的 AA 門檻。
test('ui gallery visual', async ({ page }) => {
await page.goto('/_ui');
await expect(page).toHaveScreenshot('ui-gallery.png', {
maxDiffPixels: 120, fullPage: true,
});
});
Step 6:GitHub Actions 在 PR 開啟時執行,有差異就把 diff 圖貼回 PR;有意改動才用 -u 更新基準圖,一起進 code review。
五、這個做法的代價
- 元件無法隔離開發。要看一個元件得把整個專案跑起來。
- 視覺回歸假警報不少。字型載入時機、動畫殘影、OS 字體渲染都會造成像素差,我們花約兩週才把
maxDiffPixels調到可接受,中間工程師一度忽略紅燈。 - 沒有自動生成的 API 文件。props 靠人寫註解,用 PR 模板要求仍會漏。
- 跨專案共用變困難。搬到下個案子只能複製再改。
六、不適合什麼情境
- 元件庫要給多產品/多團隊共用時。一旦需要版本號、changelog、下游訂閱更新,就該用 Storybook 加獨立 package。
- 純前端 SPA 時。我們的前提是伺服器端渲染的 Blade;React/Vue 元件本來就跟路由解耦,隔離開發的優勢放大。
- 設計師需要即時調參數時。Storybook 的 controls 面板真的好用,我們只有靜態狀態。
七、客戶為什麼該關心
品質:驗收時看到的按鈕就是上線後的按鈕,圓角、間距、hover 跟 production 同一份 CSS。
時程:過去「跟設計稿不一樣」的爭議平均耗掉三到五個工作天;改成同一份 URL 後,討論從「誰的版本算數」變成「要不要改」。
費用:我們不再收「元件庫建置維護」費,已內含在專案裡;報價改為明列基準圖工時,一次性 8 到 12 小時。
八、如果你也想這樣做
- 先確認元件真的不跨專案共用。會共用的話,這做法從第三個專案就反咬你。
- token 同步一定單向。程式端永不改 token 名稱,要改就回 Figma 重匯。
- 視覺回歸先從三個關鍵元件開始。順序是按鈕 → 表單欄位 → 卡片,穩定兩週再擴。
- PR 模板加一行「這次有沒有動到 /_ui?」沒這提醒,樣板頁三個月就過期。
移植前的自我檢查:
- ☐ 元件確定不需跨專案發版?
- ☐ Figma 已用 Variables 而非散落 styles?
- ☐ CI 能產生一致截圖?
九、用在哪些客戶身上
某北部連鎖餐飲的訂位系統:23 個 Blade 元件,/_ui 列出桌況卡片的 6 種狀態。行銷主管在驗收會上直接開這個 URL 逐項確認,兩次驗收會壓成一次。
某 B2B 工業零件商的報價後台:設計師是客戶那邊的人,單向 token 同步讓她在 Figma 改主色,重匯後半小時全站換色。
十、常見問題
那你們完全不推薦 Storybook 嗎?
不是。有專職前端、元件跨多產品、或純 React/Vue 專案,Storybook 仍是我們會建議的方案。
視覺回歸很吃 CI 時間嗎?
全頁截圖比對在 GitHub Actions 上約 40 到 90 秒;成本在維護基準圖,不在執行時間。
設計師不會寫程式,怎麼參與?
只做兩件事:在 Figma 維護 Variables、開 /_ui 看結果,編譯部署不用碰。
客戶看得懂 /_ui 嗎?
比想像中好。頁面最上方會說明「這是元件目錄,不是實際頁面」,客戶通常第二次會議就自己開來看。
樣板頁會不會變成沒人維護的孤兒?
會,如果沒有機制。我們靠 PR 模板提醒與視覺回歸失敗逼它更新。
十一、想更深入談談?
如果你正在評估設計系統,或元件庫已經跟 production 漂移到不敢打開,我們可以先看你的專案型態適不適合——不適合也會直說。
- Email:[email protected]
- 電話:0916-224-047
- LINE:@ufv9089p
另外,如果你會為了「這個 diff 算不算 bug」跟同事辯論半小時,我們一直在找人。ScriptWalker 的技術棧是 Laravel、Flutter 與 Tailwind。