內部做法

我們把 Storybook 關掉了:用一條 /_ui 路由,讓設計師、工程師、客戶看同一份真相

2026.09.18 · 11 次瀏覽
我們把 Storybook 關掉了:用一條 /_ui 路由,讓設計師、工程師、客戶看同一份真相

不再維護獨立的元件庫網站。改成專案內的真實 Blade + Tailwind 樣板頁、Figma Variables 單向同步成 tokens.json、Playwright 截圖比對把關——以及這樣做真正的代價。

分享:

一、沒有這做法時踩過的坑

兩年前替零售客戶做後台,元件庫放在獨立的 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 漂移到不敢打開,我們可以先看你的專案型態適不適合——不適合也會直說。

另外,如果你會為了「這個 diff 算不算 bug」跟同事辯論半小時,我們一直在找人。ScriptWalker 的技術棧是 Laravel、Flutter 與 Tailwind。

分享: