Communitygithub.com

cdesiles/design-system

生成两个 CSS 文件(tokens.css 和 components.css),其他应用可直接复制使用,并通过样张页面记录整个设计系统。在开始设计多屏项目或项目尚无现有系统时务必先阅读。

design-system 是什么?

该技能帮助开发者从零构建一个完整的设计系统,并将其输出为两个可被其他应用直接复制的 CSS 文件:`tokens.css`(仅含 CSS 自定义属性)和 `components.css`(基于语义化令牌构建的组件类)。同时生成一个样张页面 `Design System.html` 作为文档,供人类审查。令牌分为两层——原始值层和语义层,组件只引用语义令牌,使得品牌换肤只需修改一行令牌。规范强制遵循可访问性对比度(正文 4.5:1、大文本和 UI 边框 3:1)、使用 Oklch 颜色空间、以 4px 为基准的间距缩放、两个字体家族(展示和正文)等规则。组件列表包括按钮、输入框、卡片、导航、表格、徽章、弹窗、空状态和提示条,每个组件都展示其真实状态下的标记。分享时要求前缀命名、纯 CSS 无预处理、无全局样式污染、支持框架无关的 ARIA 状态样式。版本管理采用语义化版本,变更时同时更新两个文件的版本行。该技能适用于任何需要设计系统的前端项目,包括 React、静态页面或其他设计工具。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/cdesiles/opencode-kit/tree/HEAD/skills/design-system

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

设计系统

系统就是产物。其他一切皆构建于其上。一个自行发明颜色和间距的可交付物是一次性的;基于令牌构建的可交付物可以扩展、换肤并保持一致。

输出

三个文件,其分割本身就是关键所在:

  • design/tokens.css —— 仅包含令牌,无其他内容。纯粹的 CSS 自定义属性。
  • design/components.css —— 组件,作为仅从语义化令牌构建的类。
  • design/Design System.html —— 样张。它<link>引用了两个文件并将其记录。仅作文档:它不声明自己的任何值。

这两个 CSS 文件才是系统:其他应用将复制它们。样张保留在项目中作为其文档。

样张中的章节顺序如下:

  1. 目标 —— 三句话:为谁设计,应该带来什么感觉,明确不是什么。
  2. 颜色 —— 每个色板及其令牌名称、解析出的值和通过对比度测试的组合。
  3. 字体 —— 字体族、字重、为每个步进分配命名角色的缩放,以及正文大小的段落示例。
  4. 间距和形状 —— 间距缩放、圆角、阴影、边框。
  5. 组件 —— 每个组件以其真实状态渲染,旁边是应用复制的标记。
  6. 规则 —— 其他人可能会出错的五六个决策。
  7. 在应用中使用 —— 版本、需复制的两个文件、加载顺序、字体<link>、根类以及自上一版本以来的变更。

令牌

两层结构。原始值持有原始值;语义化令牌命名一个职责并指向一个原始值。组件只引用语义化令牌——这就是让换肤成为一行变更的关键所在。

写入design/tokens.css。这是项目中唯一可以出现原始值的文件。acme代表系统前缀(参见共享):

/* Acme 设计系统 1.0.0 — 令牌。照原样复制此文件;在其源项目中修改。 */
:root {
  /* 原始值 */
  --acme-ink-900: oklch(0.22 0.02 260);
  --acme-ink-600: oklch(0.48 0.02 260);
  --acme-paper-50: oklch(0.99 0.005 90);
  --acme-paper-100: oklch(0.96 0.008 90);
  --acme-line-200: oklch(0.90 0.006 90);
  --acme-accent-500: oklch(0.55 0.14 35);
  --acme-accent-600: oklch(0.45 0.15 35);
  --acme-accent-100: oklch(0.94 0.03 35);

  /* 语义化 */
  --acme-text: var(--acme-ink-900);
  --acme-text-muted: var(--acme-ink-600);
  --acme-bg: var(--acme-paper-50);
  --acme-surface: var(--acme-paper-100);
  --acme-border: var(--acme-line-200);
  --acme-accent: var(--acme-accent-500);
  --acme-accent-hover: var(--acme-accent-600);
  --acme-accent-wash: var(--acme-accent-100);

  /* 字体 */
  --acme-font-display: "Fraunces", Georgia, serif;
  --acme-font-text: "Public Sans", system-ui, sans-serif;
  --acme-step--1: 0.875rem;
  --acme-step-0: 1rem;
  --acme-step-1: 1.25rem;
  --acme-step-2: 1.5625rem;
  --acme-step-3: 1.953rem;
  --acme-step-4: 2.441rem;
  --acme-step-5: 3.052rem;

  /* 间距,基于 4px 基准 */
  --acme-s-1: 4px;  --acme-s-2: 8px;   --acme-s-3: 12px;  --acme-s-4: 16px;
  --acme-s-5: 24px; --acme-s-6: 32px;  --acme-s-7: 48px;  --acme-s-8: 64px;  --acme-s-9: 96px;

  /* 形状和动效 */
  --acme-radius-sm: 4px; --acme-radius: 8px; --acme-radius-lg: 16px;
  --acme-border-width: 1px; --acme-ring-width: 2px;
  --acme-shadow-1: 0 1px 2px oklch(0.2 0.02 260 / 0.06);
  --acme-shadow-2: 0 4px 16px oklch(0.2 0.02 260 / 0.08);
  --acme-ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  --acme-dur: 160ms;
}

该代码块是一个起点,而非盲目复制的模板。根据需求调整强调色相、字体组合和墨水色调——但保持结构不变。

令牌规则

  • 对比度需测量,而非目测。 正文相对于实际背景需达到 4.5:1;大文本和 UI 边框需达到 3:1。每个色板页面需说明其组合。
  • 使用 Oklch,而非随意的十六进制。 均匀的明度步进可避免调色板显得浑浊。保持同一色相家族内的色度大致恒定。
  • 无未使用的令牌。 如果没有引用某个令牌,则将其删除。
  • 此文件外无原始值。 任何在其他地方出现的十六进制、oklch() 字面量、像素大小或阴影都是一个尚未被命名的令牌。
  • 两种字体家族。 一种展示字体,一种正文字体。后备字体栈必须真实,不能仅仅是 sans-serif。
  • 使用缩放,不要即兴发挥。 如果一个值不在间距或字体缩放范围之内,要么有意添加它,要么使用最接近的值。
  • 仅当被要求时才实现暗色模式 —— 但保持颜色语义化,以便后续添加时只需替换令牌,而非重写。

组件

将它们写入 design/components.css,并在样张中每个组件的默认、悬停、聚焦、激活和禁用状态下进行渲染:

  • 按钮 —— 主要、次要、幽灵。通常一种尺寸就够了。
  • 文本输入和选择框,附带标签、帮助文本和错误状态。
  • 卡片,使用真实的内容示例,而非“Lorem ipsum”。
  • 导航,在桌面端和窄屏宽度下。
  • 表格或列表行,带有表头。
  • 徽章和标签。
  • 模态框或对话框。
  • 空状态。
  • 提示条或内联通知。

每个组件仅使用语义化令牌。如果某个组件需要没有对应令牌的值,那就是系统的一个缺口 —— 添加令牌,而不是硬编码值。

/* Acme 设计系统 1.0.0 — 组件。需要先加载同一版本的 tokens.css。 */

.acme {
  font-family: var(--acme-font-text);
  font-size: var(--acme-step-0);
  color: var(--acme-text);
  background: var(--acme-bg);
}

.acme-button {
  font: inherit;
  padding: var(--acme-s-2) var(--acme-s-4);
  border: var(--acme-border-width) solid transparent;
  border-radius: var(--acme-radius);
  background: var(--acme-accent);
  color: var(--acme-bg);
  transition: background var(--acme-dur) var(--acme-ease);
}
.acme-button:hover { background: var(--acme-accent-hover); }
.acme-button:focus-visible { outline: var(--acme-ring-width) solid var(--acme-accent); outline-offset: var(--acme-ring-width); }
.acme-button--ghost { background: transparent; color: var(--acme-accent); border-color: var(--acme-border); }

共享

其他应用通过复制tokens.css和components.css来获取系统。以下规则确保复制件在任何地方都能工作。

  • 所有内容加前缀。 在第一个令牌之前为系统选择一个简短名称,并将其应用于每个自定义属性和每个类:--acme-accent、.acme-button。复制件可以与应用自身的 CSS 共存而不会冲突。前缀是 API 的一部分;切勿更改。
  • 两个文件均使用纯 CSS。 无 Tailwind 指令、无预处理器语法、无嵌套、无 @layer 要求、两者之间无 @import。React 应用、静态页面或其他设计工具必须通过直接复制获得相同结果。
  • 一切在运行时解析。 无构建时的颜色计算,无编译输出。
  • components.css 中无全局样式。 无重置、无元素选择器、无针对 html 或 body 的样式:将文件放入应用不应改变其样式。基本文本样式存在于根类 .acme 上,应用可将其添加到需要的位置。
  • 状态来自标记。 使用 :hover、:focus-visible、:disabled、[aria-invalid="true"]、[aria-current]和[aria-expanded] 进行样式设置,而非使用应用需要切换的状态类,以便组件能与任何框架的 HTML 配合使用。
  • 易于覆盖。 尽可能每条规则只用一个类,无 ID,无 !important。
  • 两个文件版本一致。 每个文件的第一行标明系统及其版本;每次变更时同时提升版本。值变更打补丁,新增令牌或组件打次版本,删除打主版本。更名时添加新令牌并将旧令牌保留为别名 --acme-old: var(--acme-new);,直到应用迁移完毕;移除别名即为主版本升级。
  • 项目相关内容留在样张中。 字体 <link> 标签、容器宽度和资源路径不是令牌。样张展示字体 <link>,以便应用可以随文件一起复制。

如果目标项目已有令牌文件,请先匹配其命名规则,再自行发明。一个共享的词汇表比一个更好的名称更有价值。

样张页面

样张是给人类看的文档,而非供欣赏的组件画廊。在色板旁边显示令牌名称。显示每个组件及其应用要复制的标记。注明难以看到的状态(聚焦环、禁用)。

链接两个文件,令牌优先,并将根类放在 <body> 上。使用系统自身的字体和间距来布局样张 —— 这是系统能正常工作的第一个证明。

从文件中读取值,而非手动输入,这样文档就不会偏离:

<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="components.css">
…
<body class="acme">
<figure class="swatch" data-token="--acme-accent-500"></figure>
…
<script>
  for (const el of document.querySelectorAll('[data-token]')) {
    const name = el.dataset.token;
    const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
    el.dataset.value = value;                // 由 CSS 打印
    el.style.setProperty('--swatch', value); // 由 CSS 绘制
  }
</script>

色板根据令牌自绘并打印出浏览器解析后的值。手动输入的十六进制是未来的谎言。

完成

在样张上运行 design-verify。它会列出样张或 components.css 中的任何颜色字面量;应该没有。检查每个色板标签是否清晰可读,每个组件是否与同一基线网格对齐,以及是否存在示例文本在 1024px 宽度下溢出其容器的情况。

当系统迁移到其他应用时,它直接复制 tokens.css 和 components.css,加上样张中的字体 <link> —— 而非样张本身。仅在需要在没有文件夹的位置阅读样张时,才导出其独立副本。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

相关技能