是的,你可以用英語描述一個主題,並得到一個可用的設計系統。關鍵在於,這段英語描述不是提示詞。它是源檔案。和任何源檔案一樣,它需要編譯器、類型系統和測試。

如果你把它當作聊天機器人的查詢,週二得到的色板會和週一不同。如果你把它當作帶有限制上下文和嚴格模式的領域特定語言(DSL)來處理,你就能得到設計系統可以消費的、可復現的令牌。

設計令牌是同步問題,不是創意問題

大多數團隊並不苦於選色。他們苦於在Figma、CSS變數和元件庫之間保持顏色一致。典型的工作流程是這樣的:設計師在樣式指南中更新了一個十六進位碼,開發者把它複製到JSON檔案裡,另一個開發者在React元件中引用了錯誤的鍵,三個月後,一個地方是#1a1a2e,另一個地方變成了#1b1b2f

真正的痛點是交接。Figma不是程式碼。JSON不是設計工具。英語處於中間位置。它是唯一一種設計師和開發者無需培訓就能閱讀的格式。

問題不在於LLM能不能把英語轉成十六進位碼。問題在於你能不能把這個過程變得足夠可靠,以至於可以在CI中執行。

theme-to-code的實際運作方式

架構很直接。你用簡單的英語寫一份簡短的主題清單。一個指令碼以受限的輸出格式把它餵給LLM。LLM返回結構化的令牌。你把這些令牌寫入磁碟,進行類型檢查,然後提交。

以下是管線:

  1. 清單: 一份用散文描述主題的.txt.md檔案。
  2. 模式: LLM必須返回的Zod(或JSON Schema)定義。
  3. 編譯器: 一個讀取清單、以結構化輸出呼叫LLM、並寫入tokens.json的指令碼。
  4. 門禁: 一個測試,如果生成的令牌偏離快照或違反約束,就會失敗。

關鍵在於第三步。你不是讓模型「生成一個暗色主題」,而是讓它填充一個模式。這就把創意寫作任務變成了結構化資料提取任務,而LLM在這方面明顯更擅長。

一個可用的TypeScript編譯器

以下是一個最小但完整的實作。它讀取主題描述,用Zod模式呼叫OpenAI,並將結果寫入JSON檔案。

import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import OpenAI from "openai";

// 1. Define the bounded context: only these tokens exist
const TokenSchema = z.object({
  name: z.string(),
  colors: z.object({
    background: z.string().regex(/^#[0-9a-f]{6}$/i),
    surface: z.string().regex(/^#[0-9a-f]{6}$/i),
    primary: z.string().regex(/^#[0-9a-f]{6}$/i),
    text: z.string().regex(/^#[0-9a-f]{6}$/i),
    muted: z.string().regex(/^#[0-9a-f]{6}$/i),
  }),
  spacing: z.object({
    unit: z.number().min(4).max(16),
    scale: z.array(z.number()).length(4),
  }),
  radii: z.object({
    sm: z.number(),
    md: z.number(),
    lg: z.number(),
  }),
});

type TokenSet = z.infer<typeof TokenSchema>;

// 2. The compiler
async function compileTheme(
  description: string,
  apiKey: string
): Promise<TokenSet> {
  const client = new OpenAI({ apiKey });

  const completion = await client.chat.completions.create({
    model: "gpt-4o",
    messages: [
      {
        role: "system",
        content:
          "You are a design-token compiler. " +
          "Convert the user's theme description into the exact JSON schema provided. " +
          "All colors must be valid 6-digit hex. " +
          "The spacing unit must be a multiple of 4. " +
          "The scale array must have exactly 4 values.",
      },
      {
        role: "user",
        content: description,
      },
    ],
    response_format: {
      type: "json_schema",
      json_schema: {
        name: "theme_tokens",
        strict: true,
        schema: zodToJsonSchema(TokenSchema),
      },
    },
  });

  const raw = JSON.parse(completion.choices[0].message.content!);
  return TokenSchema.parse(raw);
}

// 3. CLI entrypoint
async function main() {
  const description = await Bun.file("theme.manifest.txt").text();
  const tokens = await compileTheme(description, process.env.OPENAI_API_KEY!);
  await Bun.write("tokens.json", JSON.stringify(tokens, null, 2));
  console.log("Compiled theme:", tokens.name);
}

main();

你需要zodopenaizod-to-json-schema作為依賴。指令碼假設你使用Bun,但把Bun.fileBun.write換成fs.promises是輕而易舉的。

你不能忽視的權衡

這能運作,但不是免費的。

確定性。 即使把temperature設為零並啟用結構化輸出,同一段描述在不同模型版本之間仍可能產生略有差異的十六進位碼。如果你在OpenAI更新後重新執行編譯器,snapshot test可能會失敗。你應該提交生成的tokens.json,並且只在清單變更時重新編譯。

延遲。 在構建步驟中呼叫LLM會增加數秒,有時甚至數十秒。不要在每次熱重載時都編譯主題。在清單檔案變更時,作為預提交鉤子或在CI中執行編譯器。

模式剛性。 LLM無法發明新的令牌類別。如果你的設計系統後來需要elevation.shadows.xl,你必須先更新Zod模式、系統提示詞和編譯器,模型才能輸出它。這是特性,不是缺陷。它保持了DSL的邊界。

無障礙。 LLM不知道你的主色與背景色是否滿足WCAG對比度比率。你必須用單獨的檢查來驗證生成的令牌。在TokenSchema.parse呼叫之後加入以下內容:

function contrastRatio(hex1: string, hex2: string): number {
  // WCAG relative luminance calculation
  const lum = (hex: string) => {
    const rgb = parseInt(hex.slice(1), 16);
    const [r, g, b] = [(rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff];
    const f = (c: number) => {
      c /= 255;
      return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
    };
    return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
  };
  const l1 = lum(hex1) + 0.05;
  const l2 = lum(hex2) + 0.05;
  return l1 > l2 ? l1 / l2 : l2 / l1;
}

// After parsing tokens
const ratio = contrastRatio(tokens.colors.primary, tokens.colors.background);
if (ratio < 4.5) {
  throw new Error(`Contrast ratio ${ratio.toFixed(2)} fails WCAG AA`);
}

何時會崩潰:互動問題

這種方法在配色方案、間距比例和字體族上表現優異。當令牌以難以用散文描述的方式互動時,它就會崩潰。

語義令牌是經典的陷阱。你可能會寫「使用主色作為連結和按鈕的顏色」。但停用狀態的按鈕呢?已經使用主色的警告橫幅內部的連結呢?這些不是主題層級的決策。它們是元件層級的規則,屬於元件庫,而不是清單檔案。

把清單限制在基礎令牌上。讓元件來處理語義。

常見問題

什麼是限界上下文DSL? 一種範圍狹窄的小型語言。在這個案例中,「語言」是只描述設計令牌的英語散文,受嚴格模式的約束。

同一段描述總會產生相同的令牌嗎? 不一定。LLM的輸出會因模型版本和提供商而異。提交生成的令牌,並用snapshot test來檢測漂移。

我能把它用於元件級樣式嗎? 不能。清單只應定義顏色和間距等基礎令牌。元件語義屬於元件庫。

我需要OpenAI嗎,還是其他模型也能用? 任何支援結構化JSON輸出並遵循系統提示詞的模型都能用。如果你有足夠的硬體讓本地模型在構建管線中跑得夠快,Llama 3.3這樣的本地模型也是可行的。

從十行清單和一個嚴格的模式開始

選擇一個限界上下文,比如行銷落地頁或管理後台。寫一份十行清單,定義一個不帶可選欄位的Zod模式,生成你的第一個令牌檔案。提交輸出。寫一個測試,重新編譯清單並與已提交的快照進行比較。

如果快照在清單沒有變更的情況下發生了漂移,說明你的編譯器不具備確定性。修復系統提示詞或鎖定模型版本,直到它具備確定性。

你的設計系統不需要另一個Figma外掛程式。它需要一個設計師和開發者都能閱讀的唯一真相源。簡單的英語是可讀的。模式讓它變得可靠。