AI 編程的設計規則——為什麼每個 AI 寫出來的網站都長得一樣。
Cursor、Claude Code、Cline、Windsurf——每個 AI 編程工具都掉進同一組預設。90px Inter、紫到藍漸層、三張一模一樣的卡片。換 model 也一樣,輸出幾乎沒變。ux-skill 是一個 MIT 授權的 Python 開源引擎,用 145 條確定性正則規則在 CI 中擋下預設值。不呼叫 LLM,不耗 token,沒有遙測。
為什麼每個 AI 都寫出一樣的網站
這不是 model 能力的問題。Claude Sonnet、GPT-4、DeepSeek、Gemini——只要 brief 寫得清楚,任何一個 model 都能寫出很好的 UI。問題在於沒人給 brief。「做一個 SaaS landing page」這種句子,model 就回到訓練資料的平均值——那個平均值看起來就像「2023 年的 startup landing page」。
三股力量讓預設輸出收斂。第一,訓練語料偏向流行來源——Vercel、Linear、Stripe、Tailwind UI、shadcn——這些都共享同一套美學。第二,使用者的 prompt 太短——「build a hero section」沒給出任何具體資訊。第三,沒有 gating——當 model 第一百次吐出同一個漸層時,沒有任何機制能擋下。
三股之中,前兩股難以修正——你動不了 model 的訓練語料,而每次寫 500 字 prompt 的紀律也不可能在整個團隊裡推行。第三股——gating——是機器層級的問題。這正是 ux-skill 解決的部分。
每個工具都出貨的八個預設
我們把同一份 brief——「fintech 編輯型產品的 hero,深色佈景,沉穩語氣」——丟給八個不同的 AI 編程工具(Cursor、Claude Code、Windsurf、GitHub Copilot、Cline、Continue、Codex、Aider)。八個之中,七個產出的程式碼至少命中下面八項中的五項:
- 把 Inter 當顯示字體——Inter 是 body 字體,專為小字最佳化;放大到 90px 當標題,讀起來就是「startup-landing」指紋。
- 紫到藍漸層——同樣的兩個 hex 顏色,SaaS-startup AI 輸出幾乎人人都用。
- 橫排三張一樣的卡片——features 區塊,gap-6,每張都是圖示-標題-段落。
- 通用 CTA 文案——「Get started」、「Learn more」、「Click here」。
generic-cta-text規則會抓到。 - 所有元素都套 fade-in-up——hero、features、footer 用同一條 motion;角色完全不區分。
- 空洞的行銷動詞——標題裡那種填充用的 marketing verb。
filler-marketing-verbs規則會抓到。 - 留底名稱——通用的 dummy 人名與 stub 段落,從 staging 漏到 production。
- 所有東西都置中——不管內容是什麼都是對稱排版;沒有不對稱或 bento 構圖。
完整清單在 35 條 AI 設計指紋一文——每條都附偵測正則、「為什麼」與「修法」。重點在於:選哪個 IDE 對輸出影響很小。改變設計文法才會真正改變輸出。
ux-skill 的回應——確定性的那一層
ux-skill 是 MIT 授權的 Python 套件,附 CLI 與 MCP server。同一份引擎、同一份 998 條目錄、同一個 145 條規則的 linter,三種安裝方式覆蓋 17 個 IDE:
- Claude Code 外掛——
/plugin marketplace add Laith0003/ux-skill; /plugin install ux@ux-skill - Python 套件——
pip install uxskill,任何能 shell-out 的 IDE 都能用:Cursor、Windsurf、Continue、Cline、JetBrains、Aider、Zed、Codex - MCP server——
npx uxskill@alpha mcp,可接到任何 MCP client
引擎有三個主要動作。一,uxskill discover 跑 10 欄位的 discovery protocol,擷取有結構的 brief(不允許隨機發揮——discovery protocol 是硬性需求)。二,uxskill recommend 在 998 條目錄上跑五路平行搜尋,回傳一份解算完的設計系統(色票、字體配對、motion、間距網格、元件清單)。三,uxskill lint 用 145 條正則跑程式碼。乾淨就 exit 0,有 finding 達 threshold 就 exit 1。
一條真的規則——Inter 當顯示字體
這是 145 條中的第一條,直接從 data/anti-patterns.json 拿出來。severity 是 high:
// 145 條規則之 1 { "id": "inter-as-display", "severity": "high", "category": "Typography", "detection": { "type": "regex", "pattern": "font-family:\s*['\"]?Inter['\"]?[^;}]*[;}][^{]*(?:font-size:\s*([4-9]\d|\d{3,})px|\btext-(5xl|6xl|7xl|8xl|9xl)\b)", "scope": ["css", "scss", "tsx", "jsx", "vue", "html"] }, "why": "Inter 是專為小字最佳化的 body 字體; 當顯示字體用會讀出 startup-landing 指紋。", "fix": "把 Inter (body) 配上一個有特色的 display face: Geist、Satoshi、Cabinet Grotesk、General Sans、Outfit。" }
正則找的是 font-family: Inter,且同一個區塊裡有 40px 以上的 font-size 或 Tailwind text-5xl(或更大)的 class。scope 限制規則只跑在相關副檔名上。同一條規則在 CSS、SCSS、TSX、JSX、Vue、Astro、Blade 都通用。
在 CI 中跑——GitHub Actions
一個 workflow 檔案,每個 PR push 都會被 linter 擋住。整個 repo 平均跑 380ms(冷)、90ms(熱)。不呼叫 LLM、不耗 token。
# .github/workflows/ux-lint.yml name: ux-lint on: pull_request: paths: - '**/*.{tsx,jsx,vue,css,scss,html,astro,blade}' jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: { python-version: '3.11' } - run: pip install uxskill - run: uxskill lint --threshold high
998 條目錄裡有什麼
| 類別 | 條數 | 內容 |
|---|---|---|
| 產業 | 161 | 從 logistics 到 legaltech,每個產業各有預設語氣 profile 與 pattern 組合。 |
| 風格 | 50 | 編輯電影、瑞士簡約、暖系野獸派、mid-mod、neo-pop 等。 |
| 色票 | 161 | 色彩系統——hue、saturation、contrast、dark-mode 處理。 |
| 字體配對 | 57 | display + body 組合,針對螢幕清晰度驗證過。 |
| Motion preset | 57 | Framer Motion、GSAP、CSS 三套,每套附 prefers-reduced-motion 分支。 |
| 元件 | 182 | 從卡片到對話框到儀表板,附構圖規則。 |
| 品牌規範 | 110 | Stripe、Airbnb、Vercel 等真實品牌的文法。 |
| Anti-pattern | 145 | linter 的 145 條規則。同一份 JSON,兩個 consumer。 |
| 其他分類 | 120 | 密度 profile、voice register、RTL delta。 |
| 總計 | 998 | 一棵 JSON。recommender、linter、MCP server 共讀。 |
在 AI 寫的程式碼裡,linter 是唯一確定性的那一層。其他每個檢查都是機率。
為什麼用正則,不用 LLM-judge
常見的替代方案是「用一個 LLM 當 judge」——叫 Claude 或 GPT 評分輸出,信它的分數,分低就重試。這個做法在大量使用時會出現三個複合問題。
一,不確定性。同一段輸入,judge model 不同次跑會給不同分數。CI fail 變 flake,貢獻者學會重試而不是修正,訊號崩潰。
二,昂貴。每次 PR push 都耗 token,每次 retry 都讓帳單翻倍。每週 200 個 PR 的 repo 在 diff 上跑 LLM judge,是花錢買 compute 去確認一條正則 12 毫秒內就能抓到的東西。
三,藏掉規則。judge 失敗時,作者收到的是散文。正則失敗時,作者收到的是一個可以對照自己程式碼學習的 pattern。前者催生申訴文化,後者催生熟練文化。
誠實的星數比較
bolt-on 類別中三個認真的開源專案。GitHub stars 為 2026-05-28 即時數據:
| 專案 | Stars | recommender | linter | MCP | 目錄 |
|---|---|---|---|---|---|
| ui-ux-pro-max | 84k | 有 | 無 | 無 | ~600 |
| taste-skill | 25k | 有 | 無 | 無 | ~480 |
| ux-skill | 14 | 有 | 145 條 | 14 個 tool | 998 |
ux-skill 在 stars 上最小(14),在功能面最大。998 條目錄、145 條正則規則在 CI 中以 non-zero exit 觸發、14-tool MCP server 跨 17 個 IDE、22 個指令、110 條品牌規範作為參考文法、75 個測試。ui-ux-pro-max 與 taste-skill 都只出貨 recommender。ux-skill 同時出貨 recommender + linter + MCP。這是不對稱的下棋。
正則抓的是結構性指紋,不是品味。
linter 永遠不會講「這個區塊節奏不對」或「這段文案讀起來太冷」。那需要人類或一個 model。但正則確實會抓到所有有 literal token、pattern 或形狀的指紋——AI 預設大多都是這類,因為 model 一再回到同一個 artifact。
我們刻意不在 CI 跑 LLM-judge。如果你的標準是「看起來像 designer 做的」——那是 code review,不是 lint。ux-skill 有出貨 /ux-critique 做品味檢查,但那是在 IDE 中跑,不是 CI。
下一步讀什麼
想看完整的 145 條規則 manifest 加上一個真的 Next.js hero 的 before/after,讀 regex linter 一文。想看完整 taxonomy 與每條規則的偵測正則,讀 35 條 AI 設計指紋一文。想看 2026 年 AI 編程工具的設計品質完整排名,讀 anti-AI-slop ranking 一文。