是的,你可以用英语描述一个主题,并得到一个可用的设计系统。关键在于,这段英语描述不是提示词。它是源文件。和任何源文件一样,它需要编译器、类型系统和测试。

如果你把它当作聊天机器人的查询,周二得到的色板会和周一不同。如果你把它当作带有有限上下文和严格模式的领域特定语言(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更新后重新运行编译器,快照测试可能会失败。你应该提交生成的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的输出会因模型版本和提供商而异。提交生成的令牌,并用快照测试来检测漂移。

我能把它用于组件级样式吗? 不能。清单只应定义颜色和间距等基础令牌。组件语义属于组件库。

我需要OpenAI吗,还是其他模型也能用? 任何支持结构化JSON输出并遵循系统提示词的模型都能用。如果你有足够的硬件让本地模型在构建流水线中跑得够快,Llama 3.3这样的本地模型也是可行的。

从十行清单和一个严格的模式开始

选择一个限界上下文,比如营销落地页或管理后台。写一份十行清单,定义一个不带可选字段的Zod模式,生成你的第一个令牌文件。提交输出。写一个测试,重新编译清单并与已提交的快照进行比较。

如果快照在清单没有变更的情况下发生了漂移,说明你的编译器不具备确定性。修复系统提示词或锁定模型版本,直到它具备确定性。

你的设计系统不需要另一个Figma插件。它需要一个设计师和开发者都能阅读的唯一真相源。简单的英语是可读的。模式让它变得可靠。