用 Claude Code 打造生产级 CSS:架构、Tokens 与视觉回归
用 Claude Code 整理生产级 CSS:层叠层、tokens、容器查询、深色模式、可访问性和视觉回归。
CSS 很适合交给 Claude Code,但生产环境的样式不只是“看起来差不多”。 你还需要 CSS 架构、可复用 tokens、响应式布局、深色模式、可访问性和回归检查。 这篇文章用一个定价卡片作为例子,说明如何让 Claude Code 写得快,同时不把样式表变成临时补丁的集合。
这里先把术语说清楚。设计 token 是颜色、间距、圆角和阴影等设计决策的命名变量。层叠层是把 CSS 优先级放进不同“货架”的机制。容器查询不是看整个屏幕宽度,而是看组件所在容器的宽度。把这些定义写进提示词,Claude Code 更容易沿着项目现有体系工作。
建议同时打开官方资料:MDN @layer、CSS 自定义属性、容器查询、prefers-color-scheme、W3C 对比度说明和 Playwright 截图比较。Claude Code 的流程参考 Claude Code common workflows。站内可以继续看 CLAUDE.md 最佳实践和测试策略。
先建立样式工作的边界
如果你只说“帮我整理 CSS”,Claude Code 可能会很快让页面变好看,但长期维护会更难。真实项目经常混合全局 CSS、CSS Modules、Tailwind utility、Markdown 内容样式、浏览器默认样式和旧的覆盖规则。没有边界时,助手容易加更强的选择器,而不是修正架构。
第一步是建立 harness,也就是“智能体工作的安全框架”。你需要告诉它目标文件、允许修改的层、token 命名规则、必须运行的命令,以及不能碰的区域。样式改动容易越做越大,所以这个框架会让 diff 保持可审查。
flowchart LR
P["Claude Code 提示词"] --> L["cascade layers"]
L --> T["design tokens"]
T --> C["card/button components"]
C --> R["responsive + container queries"]
R --> D["dark mode"]
D --> A["accessibility checks"]
A --> V["Playwright visual regression"]
先让 Claude Code 只调查,不编辑:
Read AGENTS.md, CLAUDE.md, package.json, and every file under src/styles.
Do not edit yet.
Report the current CSS architecture, naming conventions, token usage,
dark-mode strategy, responsive breakpoints, and test commands.
Then propose the smallest safe plan for a pricing card and CTA button.
这个提示词可以避免一个常见错误:Claude Code 不读取现有系统,直接新建一套写法。生产站点可能同时有 reset.css、global.css、组件样式和 CMS 内容样式。边界由人来定,Claude Code 的职责是在边界内实现。
用层叠层固定优先级
层叠决定多个规则命中同一元素时谁生效。团队常用更高详细度、靠后的导入顺序或 !important 来解决冲突。短期能发布,长期会让下一次修改更难。@layer 可以把优先级的货架命名出来。
下面的 app.css 可以直接复制到小型验证中使用。它把 tokens、base、components、utilities 和 overrides 分开。overrides 应该是临时隔离区,不是正常功能层。
/* src/styles/app.css */
@layer reset, tokens, base, components, utilities, overrides;
@import "./tokens.css" layer(tokens);
@import "./base.css" layer(base);
@import "./components.css" layer(components);
@import "./utilities.css" layer(utilities);
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
body,
h1,
h2,
h3,
p {
margin: 0;
}
}
@layer overrides {
.legacy-markdown :where(table, pre) {
max-width: 100%;
}
}
让 Claude Code 先分类,再重写:
Move existing global CSS into the layer model in src/styles/app.css.
Do not change class names used by templates.
Use reset, tokens, base, components, utilities, and overrides only.
If a rule must go into overrides, explain why in the final response.
Run npm test and the visual check command after editing.
坑点是半迁移。没有放进 layer 的普通规则仍然可能压过 layer 内规则,@import 也有自己的位置要求。不要只改一小段就以为层叠已经清晰。先在一个新组件上验证,再分批迁移旧 CSS。
用 CSS 变量保存设计 tokens
Tokens 不是装饰,它们能阻止 Claude Code 每次都发明新的绿色、新的阴影和新的按钮圆角。通俗地说,token 就是一个被命名的设计决策。
/* src/styles/tokens.css */
@layer tokens {
:root {
color-scheme: light;
--color-bg: #f7f7f2;
--color-surface: #ffffff;
--color-text: #1f2933;
--color-muted: #5d6673;
--color-border: #d9ded7;
--color-accent: #0f766e;
--color-accent-strong: #0b4f49;
--color-focus: #b45309;
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--shadow-card: 0 0.75rem 2rem rgb(31 41 51 / 0.12);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--color-bg: #111827;
--color-surface: #1f2937;
--color-text: #f9fafb;
--color-muted: #cbd5e1;
--color-border: #475569;
--color-accent: #2dd4bf;
--color-accent-strong: #99f6e4;
--color-focus: #fbbf24;
--shadow-card: 0 0.75rem 2rem rgb(0 0 0 / 0.32);
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--color-bg: #111827;
--color-surface: #1f2937;
--color-text: #f9fafb;
--color-muted: #cbd5e1;
--color-border: #475569;
--color-accent: #2dd4bf;
--color-accent-strong: #99f6e4;
--color-focus: #fbbf24;
--shadow-card: 0 0.75rem 2rem rgb(0 0 0 / 0.32);
}
}
生产环境不要只让 Claude Code “换成更好看的颜色”。要明确要求检查对比度。WCAG 对普通文本通常要求至少 4.5:1 的对比度,所以正文、弱化文本、链接、按钮和焦点环都要在明暗主题里一起看。
把卡片和按钮限制在组件层
卡片和按钮会出现在博客、SaaS、设置页、结账页和后台界面里。如果 Claude Code 新增 .title、.button 这种泛用类,迟早会影响别的页面。类名要能看出归属。
/* src/styles/components.css */
@layer components {
.ui-card {
container: card / inline-size;
display: grid;
gap: var(--space-4);
padding: var(--space-6);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: var(--color-surface);
color: var(--color-text);
box-shadow: var(--shadow-card);
}
.ui-card__eyebrow {
color: var(--color-accent);
font-size: 0.875rem;
font-weight: 700;
}
.ui-card__title {
max-width: 18ch;
font-size: clamp(1.5rem, 1rem + 2cqi, 2.25rem);
line-height: 1.1;
}
.ui-card__body {
color: var(--color-muted);
font-size: 1rem;
line-height: 1.7;
}
.ui-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
align-items: center;
}
.ui-button {
display: inline-flex;
min-height: 2.75rem;
align-items: center;
justify-content: center;
padding: 0.75rem 1rem;
border: 1px solid transparent;
border-radius: var(--radius-sm);
background: var(--color-accent);
color: var(--color-surface);
font: inherit;
font-weight: 700;
text-decoration: none;
}
.ui-button:hover {
background: var(--color-accent-strong);
}
.ui-button:focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 3px;
}
.ui-button[aria-disabled="true"],
.ui-button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
}
再准备一个稳定的预览页面。无论是 Astro、Next.js 还是 Vite,都可以在 /style-lab 放这段内容,方便 Playwright 找到固定目标。
<main class="style-lab">
<article class="ui-card" data-testid="pricing-card">
<p class="ui-card__eyebrow">Team plan</p>
<h1 class="ui-card__title">Ship production CSS with Claude Code</h1>
<p class="ui-card__body">
Layer your CSS, reuse tokens, check dark mode, and catch visual regressions before release.
</p>
<div class="ui-actions">
<a class="ui-button" href="/training/">Start with training</a>
<a class="ui-button" href="/thanks/">Get the free checklist</a>
</div>
</article>
</main>
视觉测试可以用 data-testid 定位,交互和可访问性测试则优先用 role、label 和可见名称。这样文案微调不会让视觉测试失效,用户真实操作路径也能被覆盖。
区分视口规则和容器规则
媒体查询看的是整个视口。容器查询看的是组件可用空间。同一个卡片放在全宽落地页、窄侧栏和仪表盘网格里时,这个差异很重要。
/* src/styles/responsive.css */
@layer components {
.style-lab {
display: grid;
min-height: 100svh;
place-items: center;
padding: clamp(1rem, 4vw, 4rem);
background: var(--color-bg);
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: var(--space-6);
width: min(100%, 72rem);
}
@container card (min-width: 36rem) {
.ui-card {
grid-template-columns: 1fr auto;
align-items: center;
}
.ui-card__body {
max-width: 58ch;
}
.ui-actions {
justify-content: end;
}
}
@media (max-width: 40rem) {
.ui-card {
padding: var(--space-4);
}
.ui-actions,
.ui-button {
width: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
}
}
}
不要只写“做响应式”。更好的提示是:在 320、375、768、1024 和 1440 像素检查横向滚动、文字换行、焦点环裁切、按钮高度和深色模式布局。具体宽度会让 Claude Code 输出验证过的差分,而不是只改外观。
三个以上实际用例
用例一是博客或落地页的变现卡片。广告、联盟链接、咨询 CTA 和免费下载 CTA 需要醒目,但不能造成布局偏移或视觉噪音。可以要求 Claude Code 在同一个小变更里完成组件 CSS、响应式、深色模式和视觉回归测试。
用例二是 SaaS 设置页。卡片、表单、危险操作按钮和保存状态经常共用一个 surface。如果每个页面都发明自己的颜色,未来品牌调整会很贵。要求 Claude Code 只使用现有 tokens;如果缺少 token,先提出候选,不要直接编辑。
用例三是 CMS 旧内容样式。Markdown 和 MDX 正文常有 h2、table、pre、blockquote 的全局规则。批量重写会破坏很多文章。把改动限制在 .legacy-markdown 包裹内,并对几篇已发布文章做截图比较。
用例四是设计系统迁移的第一步。不要一次替换所有组件。从卡片和按钮这类高频组件开始。diff 越小,Claude Code 越容易贴合项目风格,审查者也越容易信任。
审查时重点抓的坑
第一个坑是 !important。它能让一次发布看起来解决了问题,但会制造更强的后续冲突。提示词里应写明不要添加 !important;如果看起来必须使用,就解释选择器冲突。
第二个坑是只做深色背景,不检查对比度。正文、弱化文本、链接、按钮、边框和焦点环要一起工作,尤其不要把辅助文本调得过淡。
第三个坑是所有布局都按视口宽度判断。侧栏里的卡片在宽屏上仍然可能很窄。这个场景应该用容器查询,而不是继续增加断点。
第四个坑是视觉测试不稳定。外部字体、动画、随机数据和实时广告都会让截图因为错误原因失败。先使用固定内容、减少动画,再比较像素。
用 Playwright 做视觉回归
CSS 不能只靠人工肉眼检查。Playwright 截图比较可以持续检查不同宽度、明亮模式、深色模式和键盘焦点状态。
// tests/visual/style-regression.spec.ts
import { expect, test } from "@playwright/test";
const viewports = [
{ name: "mobile", width: 375, height: 812 },
{ name: "tablet", width: 768, height: 1024 },
{ name: "desktop", width: 1440, height: 900 },
];
for (const viewport of viewports) {
test(`pricing card visual regression ${viewport.name}`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await page.emulateMedia({ colorScheme: "light", reducedMotion: "reduce" });
await page.goto("/style-lab");
const card = page.getByTestId("pricing-card").first();
await expect(card).toBeVisible();
await expect(card).toHaveScreenshot(`pricing-card-${viewport.name}-light.png`, {
animations: "disabled",
maxDiffPixelRatio: 0.02,
});
});
}
test("dark theme keeps focus states visible", async ({ page }) => {
await page.emulateMedia({ colorScheme: "dark", reducedMotion: "reduce" });
await page.goto("/style-lab");
await page.locator("html").evaluate((element) => {
element.setAttribute("data-theme", "dark");
});
const startButton = page.getByRole("link", { name: /start with training/i });
await startButton.focus();
await expect(startButton).toBeFocused();
await expect(page.getByTestId("pricing-card").first()).toHaveScreenshot(
"pricing-card-dark-focus.png",
{
animations: "disabled",
maxDiffPixelRatio: 0.02,
},
);
});
首次生成快照,之后正常检查:
npx playwright test tests/visual/style-regression.spec.ts --update-snapshots
npx playwright test tests/visual/style-regression.spec.ts
最后让 Claude Code 做固定格式的批判性审查:
Critically review the CSS diff.
Check cascade layers, token usage, selector specificity, dark mode,
container queries, keyboard focus, color contrast, reduced motion,
and Playwright visual coverage.
Return only concrete issues with file paths and line numbers.
变现 CTA 与实际测试结果
CSS 技术文章不应该只停在代码示例。个人开发者可以从免费清单开始,团队负责人可以进入 Claude Code 培训与咨询。如果想把规则做成可重复流程,可以继续读Harness Engineering。
我实际用这个流程测试时,最大收益不是 @layer 本身,而是开头的调查提示词。先让 Claude Code 阅读现有样式、tokens 和验证命令后,diff 基本限制在卡片和按钮内。只说“把样式变好看”时,它会增加硬编码颜色、重复间距,而且没有视觉检查。Claude Code 可以很快写 CSS,但生产质量来自人先固定的架构、token 名称和回归命令。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code权限安全阶梯:逐步放开访问而不失控
从只读到有限编辑、验证命令和部署检查的 Claude Code 权限升级流程。
Claude Code 小PR证据包:让小改动真正可审查
用差异、验证命令、公开URL、CTA路径和回滚说明,把Claude Code的小PR变得可审查。
Claude Code 提交前 Review Gate:同时检查差异、测试、公开 URL 和 CTA
提交前用 Claude Code 审查差异范围、build、公开 URL、Gumroad 链接、咨询 CTA、缺少测试和无关文件。