是的,你可以用英語描述一個主題,並得到一個可用的設計系統。關鍵在於,這段英語描述不是提示詞。它是源檔案。和任何源檔案一樣,它需要編譯器、類型系統和測試。
如果你把它當作聊天機器人的查詢,週二得到的色板會和週一不同。如果你把它當作帶有限制上下文和嚴格模式的領域特定語言(DSL)來處理,你就能得到設計系統可以消費的、可復現的令牌。
設計令牌是同步問題,不是創意問題
大多數團隊並不苦於選色。他們苦於在Figma、CSS變數和元件庫之間保持顏色一致。典型的工作流程是這樣的:設計師在樣式指南中更新了一個十六進位碼,開發者把它複製到JSON檔案裡,另一個開發者在React元件中引用了錯誤的鍵,三個月後,一個地方是#1a1a2e,另一個地方變成了#1b1b2f。
真正的痛點是交接。Figma不是程式碼。JSON不是設計工具。英語處於中間位置。它是唯一一種設計師和開發者無需培訓就能閱讀的格式。
問題不在於LLM能不能把英語轉成十六進位碼。問題在於你能不能把這個過程變得足夠可靠,以至於可以在CI中執行。
theme-to-code的實際運作方式
架構很直接。你用簡單的英語寫一份簡短的主題清單。一個指令碼以受限的輸出格式把它餵給LLM。LLM返回結構化的令牌。你把這些令牌寫入磁碟,進行類型檢查,然後提交。
以下是管線:
- 清單: 一份用散文描述主題的
.txt或.md檔案。 - 模式: LLM必須返回的Zod(或JSON Schema)定義。
- 編譯器: 一個讀取清單、以結構化輸出呼叫LLM、並寫入
tokens.json的指令碼。 - 門禁: 一個測試,如果生成的令牌偏離快照或違反約束,就會失敗。
關鍵在於第三步。你不是讓模型「生成一個暗色主題」,而是讓它填充一個模式。這就把創意寫作任務變成了結構化資料提取任務,而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();
你需要zod、openai和zod-to-json-schema作為依賴。指令碼假設你使用Bun,但把Bun.file和Bun.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外掛程式。它需要一個設計師和開發者都能閱讀的唯一真相源。簡單的英語是可讀的。模式讓它變得可靠。