如何不貼上整份主題檔,仍限制 AI 智能體使用設計 token 編寫 CSS
Constraining agent CSS to design tokens (without pasting the whole theme)
作者提出一套限制 AI 智能體 CSS 使用設計 token 的做法,不必把整份 tokens.css 放進提示詞。做法包括提供約 40–60 行的 token 白名單、明列禁止原始值及自創 token,並以 CI 或 ESLint 檢查;超出既有尺度的設計要求應列為阻礙事項,而非自行近似。
I used to drop the entire tokens.css into the agent prompt so it “wouldn’t invent colors.” It still invented colors. Just prettier ones — #3B82F6 instead of blue, 14px instead of space.3, a one-off radius that almost matched radius.md.
Pasting the whole theme teaches the model what exists. It does not teach it that everything else is illegal. Agents treat big token dumps like a style moodboard, then freestyle.
Here’s the thinner constraint that actually stuck in our agent UI PRs.
The failure mode: theme dump
A prompt like this looks responsible and fails quietly:
Here is our full design token file (800 lines of CSS variables). Style this settings panel to match.
What came back:
- Hex that “felt close” to
--color-accent-primary -
padding: 18pxbecause “the mock looked about that” - A new
--card-shadow-softdeclared inline in the component - Tailwind arbitrary values (
bg-[#0f172a]) next to token classes
The model saw the tokens. It optimized for “looks like our product,” not “uses only the legal surface.”
What “token-constrained CSS” means
I define it as four hard rules the agent can fail against:
-
No raw values — no hex, rgb, hsl, or bare
px/remfor color, space, radius, or shadow in changed CSS/JSX -
Import / reference allowlist — only named tokens from the published set (
var(--space-4),theme('spacing.4'),tokens.space[4], whatever your stack uses) -
No local token invention — do not declare new
--*custom properties in feature CSS unless the ticket says “new token approved” - Closest-token rule — if the design asks for something off-scale, stop and name the gap; don’t invent a near-miss
If the prompt only says “use our design system,” the agent hears “make it pretty.” Name the bans.
The slim token pack I actually paste
Instead of the whole theme, I keep a token cheat sheet under ~40–60 lines and refresh it when the public scale changes:
## Legal tokens (CSS / Tailwind / JS — same names)
### Color
- bg.default, bg.subtle, bg.muted
- fg.default, fg.muted, fg.danger
- border.default, border.strong
- accent.primary, accent.primary-hover
Forbidden: raw hex/rgb, inventing --color-*, opacity hacks that bake a new tint
### Space
- 1 2 3 4 5 6 8 10 12 (map to --space-* or spacing.*)
Forbidden: 13px, 18px, 22px, “eyeballed” padding, gap: 7
### Radius
- sm | md | lg | full
Forbidden: border-radius: 10px, rounded-[10px]
### Shadow
- shadow.sm | shadow.md | shadow.lg
Forbidden: one-off box-shadow stacks in feature CSS
### Type
- font.size: sm | md | lg | xl
- font.weight: regular | medium | semibold
Forbidden: font-size: 15px, font-weight: 450
## How to reference (pick your stack)
- CSS: var(--space-4), var(--color-fg-muted)
- Tailwind: p-4, text-fg-muted, rounded-md (token-mapped utilities only)
- JS/CSS-in-JS: tokens.space[4], tokens.color.fg.muted
## Acceptance (must pass before PR)
- [ ] Zero hex/rgb/hsl in the diff
- [ ] Zero bare px for space/radius/type (layout hacks like 1px hairlines need a one-line NOTE)
- [ ] Zero new --* declarations outside tokens/
- [ ] Any off-scale design ask is listed as a blocker, not approximated
That’s the whole “theme” most feature tasks get. The full tokens.css stays in the repo for humans and for codegen — the prompt carries the decision surface.
Prompt shape that works
My default ask looks like this:
Style X using only the Legal tokens below.
Do not invent colors, spacing, radius, or shadows.
If a value isn’t on the scale, stop and name the missing token — don’t approximate.
Prefer existing utility / token classes over new CSS files.
When done, paste the Acceptance checklist with pass/fail and list every token you used.
I also add one explicit ban that cut a lot of noise:
Forbidden: Tailwind arbitrary values for color/space/radius (
bg-[#…],p-[18px],rounded-[10px]). If the utility doesn’t exist, the token doesn’t exist for this task.
Arbitrary values are how agents smuggle freestyle CSS past a “we use Tailwind” rule.
A tiny CI gate that catches the same mistakes
Prompts leak. I pair them with a cheap lint on agent-touched paths:
# scripts/check-no-raw-css-values.sh — run on changed UI files
rg -n --pcre2 \
'(#[0-9a-fA-F]{3,8}\b|rgba?\(|hsla?\(|\b\d+(\.\d+)?px\b|rounded-\[[^\]]+\]|bg-\[[^\]]+\]|p-\[[^\]]+\]|gap-\[[^\]]+\])' \
src/components src/app \
-g '!**/tokens/**' \
-g '!**/*.test.*' \
&& exit 1 || exit 0
False positives happen (SVG path data, 1px hairlines). Allowlist those with a comment:
/* RAW_OK: 1px hairline separator — not a spacing token */
border-top: 1px solid var(--color-border-default);
The point isn’t zero exceptions. It’s that every exception is named, so agent PRs can’t quietly grow a second palette.
For Tailwind shops, eslint-plugin-tailwindcss with no-arbitrary-value (or a custom restrict) on src/features/** works even better — fail the PR when the model reaches for [#0f172a].
What I put in the PR template
A short checkbox beats another paragraph in the system prompt:
### Token compliance
- [ ] No raw color / space / radius / shadow values in the diff
- [ ] No new --* tokens outside tokens/
- [ ] Off-scale design requests called out (not approximated)
- [ ] `scripts/check-no-raw-css-values.sh` (or ESLint) passed
Reviewers stop arguing taste (“that blue is fine”) and start enforcing the scale.
Review comments I actually leave
When I see freestyle CSS in an agent diff:
Token miss:
padding: 18pxisn’t on the space scale. Usevar(--space-4)/p-4or stop and request a new token. Don’t approximate.
When the model invents a local variable:
New
--card-shadow-softbelongs intokens/with a design OK, not in the feature file. Revert and useshadow.mdor file a token RFC.
When Tailwind arbitrary values show up:
bg-[#0f172a]is a banned escape hatch here. Map tobg-subtle/bg-mutedor name the missing token. Arbitrary color values fail the token gate on purpose.
What I stopped doing
- Pasting the entire theme “for completeness”
- Saying “match the design system” without listing banned value types
- Trusting the model to “prefer tokens” without a lint gate
- Letting one-off hex land because “it’s only in this panel”
Constraining agent CSS to design tokens is less about teaching every variable name and more about shrinking the legal alphabet. Give a short allowlist, ban raw values out loud, and fail CI when freestyle sneaks in. The full theme can stay in the repo — it just doesn’t need to live in every prompt.
來源:DEV Community · dev.to