Primitive Tokens 深度解析:设计系统的原始地基
2026/9/18 2:31:23 网站建设 项目流程

Primitive Tokens 深度解析:设计系统的原始地基

【免费下载链接】ui-ux-pro-max-skillAn AI skill that provides design intelligence for building professional UI/UX across multiple platforms.项目地址: https://gitcode.com/gh_mirrors/ui/ui-ux-pro-max-skill

Primitive Tokens(原始设计令牌)是任何设计系统最底层的"地基"——它们定义了一组不带语义含义的原始设计值(颜色、间距、字号、圆角、阴影、动效时长、层级等),上层所有语义令牌(Semantic Tokens)和组件令牌(Component Tokens)都通过var()引用它们。本文基于 ui-ux-pro-max-skill 仓库中 primitive-tokens.md 的完整定义,逐一拆解每个原始令牌族的作用、命名约定、使用场景与底层实现支撑。读完本文,你将掌握:如何搭建一套可扩展的 primitive token 体系、如何理解它与语义层/组件层的关系、如何用仓库配套脚本生成与校验令牌,以及如何避免最常见的"硬编码污染"陷阱。

1. 什么是 Primitive Tokens

在 token-architecture.md 定义的三层令牌体系中,Primitive Tokens 位于最底层:

┌─────────────────────────────────────────┐ │ Component Tokens │ Per-component overrides │ --button-bg, --card-padding │ ├─────────────────────────────────────────┤ │ Semantic Tokens │ Purpose-based aliases │ --color-primary, --spacing-section │ ├─────────────────────────────────────────┤ │ Primitive Tokens │ Raw design values │ --color-blue-600, --space-4 │ └─────────────────────────────────────────┘

核心特征:Primitive Tokens 是"不带语义的原始值"。--color-blue-600: #2563EB只表达"这是蓝色系第 600 号色值",而不表达"这个颜色应该用在按钮上"。语义层(--color-primary: var(--color-blue-600))和组件层(--button-bg: var(--color-primary))才负责"含义赋值"。

层级用途何时变更
Primitive基础值(颜色、尺寸)极少变更——它们是地基
Semantic语义赋值主题切换(亮/暗)
Component组件定制按组件需求定制

这带来的核心收益是可替换性:当品牌从蓝色换为绿色时,只需改动 primitive 层的--color-blue-*系列值,语义层与组件层无需任何修改;同理,暗色主题只需在.dark作用域重定义语义令牌,primitive 层原封不动。

2. 颜色令牌(Color Scales)

2.1 灰色阶(Gray Scale)

灰色阶是全套令牌中使用频率最高的色系,承担背景、边框、次级文字等大量中性角色。primitive 文档给出从 50 到 950 的完整梯度:

:root { --color-gray-50: #F9FAFB; --color-gray-100: #F3F4F6; --color-gray-200: #E5E7EB; --color-gray-300: #D1D5DB; --color-gray-400: #9CA3AF; --color-gray-500: #6B7280; --color-gray-600: #4B5563; --color-gray-700: #374151; --color-gray-800: #1F2937; --color-gray-900: #111827; --color-gray-950: #030712; }

数字越小越浅、越大越深。在语义层中,这组值被大量引用:--color-background: var(--color-gray-50)(亮色背景)、--color-foreground: var(--color-gray-900)(正文前景)、--color-border: var(--color-gray-200)(边框)、--color-muted-foreground: var(--color-gray-500)(弱化文字)等,详见 semantic-tokens.md。

提示:primitive 层的灰阶在亮/暗双主题中扮演"镜像"角色——暗色模式下--color-background变为--color-gray-950--color-foreground变为--color-gray-50,而 primitive 值本身不变,这正是"原始值不动、语义值变换"的典型体现。

2.2 主色系(Primary Colors - Blue)

主色选用蓝色系,提供 50–900 九个色阶:

:root { --color-blue-50: #EFF6FF; --color-blue-100: #DBEAFE; --color-blue-200: #BFDBFE; --color-blue-300: #93C5FD; --color-blue-400: #60A5FA; --color-blue-500: #3B82F6; --color-blue-600: #2563EB; --color-blue-700: #1D4ED8; --color-blue-800: #1E40AF; --color-blue-900: #1E3A8A; }

其中 500/600/700/800 在交互状态中承担关键角色:

  • --color-primary: var(--color-blue-600)—— 主要操作色
  • --color-primary-hover: var(--color-blue-700)—— hover 加深
  • --color-primary-active: var(--color-blue-800)—— 按下再加深
  • --color-ring: var(--color-blue-500)—— 焦点环

为什么每个色系要保持完整梯度?因为语义层与组件层需要"同色系不同明度"来表达交互状态。若只用单一色值,hover/active/focus 状态将无差异化,组件将失去可感知的反馈层次。

2.3 状态色(Status Colors)

状态色直接映射到产品级反馈场景,primitive 层只定义 500/600 两档,语义层再补充 foreground:

:root { /* Success - Green */ --color-green-500: #22C55E; --color-green-600: #16A34A; /* Warning - Yellow */ --color-yellow-500: #EAB308; --color-yellow-600: #CA8A04; /* Error - Red */ --color-red-500: #EF4444; --color-red-600: #DC2626; /* Info - Blue */ --color-info: var(--color-blue-500); }

注意--color-info这里直接引用了主色系的 blue-500——这说明 primitive 层允许跨色系引用,它只是"原始值仓库",不承担语义命名职责。语义层在此基础上完成最终映射:

--color-success: var(--color-green-600); --color-warning: var(--color-yellow-500); --color-error: var(--color-red-600); --color-destructive: var(--color-red-600);

3. 间距令牌(Spacing Scale)

3.1 4px 基准单位系统

primitive 文档明确说明间距采用4px 基准单位系统,所有间距值都是 4px 的整数倍(含 0.5 倍档位):

:root { --space-0: 0; --space-px: 1px; --space-0-5: 0.125rem; /* 2px */ --space-1: 0.25rem; /* 4px */ --space-1-5: 0.375rem; /* 6px */ --space-2: 0.5rem; /* 8px */ --space-2-5: 0.625rem; /* 10px */ --space-3: 0.75rem; /* 12px */ --space-3-5: 0.875rem; /* 14px */ --space-4: 1rem; /* 16px */ --space-5: 1.25rem; /* 20px */ --space-6: 1.5rem; /* 24px */ --space-7: 1.75rem; /* 28px */ --space-8: 2rem; /* 32px */ --space-9: 2.25rem; /* 36px */ --space-10: 2.5rem; /* 40px */ --space-12: 3rem; /* 48px */ --space-14: 3.5rem; /* 56px */ --space-16: 4rem; /* 64px */ --space-20: 5rem; /* 80px */ --space-24: 6rem; /* 96px */ }

为什么用 rem 而非 px?rem 以根元素字号为基准,用户调整浏览器默认字号(无障碍场景常见)时,间距、字号会等比缩放,保证整体比例不变。注释中的 px 值仅供设计还原参考。

使用规律:4px 整数倍使不同组件、不同页面之间的留白保持视觉节奏统一。语义层在此基础上定义--spacing-component(组件内距)、--spacing-section(区块间距)、--spacing-page-x/y(页面边距)等更高层别名。真实工程中,用--space-*替换散落的margin: 12pxpadding: 24px是消除"硬编码污染"的第一步。

4. 字体令牌(Typography Scale)

4.1 字号(Font Sizes)

:root { --font-size-xs: 0.75rem; /* 12px */ --font-size-sm: 0.875rem; /* 14px */ --font-size-base: 1rem; /* 16px */ --font-size-lg: 1.125rem; /* 18px */ --font-size-xl: 1.25rem; /* 20px */ --font-size-2xl: 1.5rem; /* 24px */ --font-size-3xl: 1.875rem; /* 30px */ --font-size-4xl: 2.25rem; /* 36px */ --font-size-5xl: 3rem; /* 48px */ }

该梯度与 Tailwind 默认字号刻度完全一致,便于后续映射到 Tailwind 主题(见 tailwind-integration.md)。语义层基于它构建标题/正文/标签体系:

--font-heading: var(--font-size-2xl); --font-heading-lg: var(--font-size-3xl); --font-body: var(--font-size-base); --font-caption: var(--font-size-xs);

4.2 行高(Line Heights)

:root { --leading-none: 1; --leading-tight: 1.25; --leading-snug: 1.375; --leading-normal: 1.5; --leading-relaxed: 1.625; --leading-loose: 2; }

行高使用无单位的倍数而非固定像素,这是最佳实践:行高与字号成比例,字号变化时行距自动适配。标题通常用 tight/snug(紧凑),正文用 normal/relaxed(舒展),引用块或说明文字可用 loose。

4.3 字重(Font Weights)

:root { --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }

字重同样以数值令牌抽象,避免在组件中直接写font-weight: 600。组件层典型用法是--button-font-weight: var(--font-weight-medium)

4.4 字间距(Letter Spacing)

:root { --tracking-tighter: -0.05em; --tracking-tight: -0.025em; --tracking-normal: 0; --tracking-wide: 0.025em; --tracking-wider: 0.05em; }

字间距使用em单位(相对字号),常用场景:大标题压缩字距(tight/tighter)提升紧凑感,小号标签或全大写按钮放宽字距(wide/wider)提升可读性。

5. 圆角令牌(Border Radius)

:root { --radius-none: 0; --radius-sm: 0.125rem; /* 2px */ --radius-default: 0.25rem; /* 4px */ --radius-md: 0.375rem; /* 6px */ --radius-lg: 0.5rem; /* 8px */ --radius-xl: 0.75rem; /* 12px */ --radius-2xl: 1rem; /* 16px */ --radius-3xl: 1.5rem; /* 24px */ --radius-full: 9999px; }

圆角刻度从 0 到胶囊形(9999px)。组件层映射示例:按钮--button-radius: var(--radius-md)、卡片--card-radius: var(--radius-lg)、徽章--badge-radius: var(--radius-full)--radius-full用于胶囊按钮、Tag、头像等完全圆角元素。

注意:primitive 文档中--radius-default: 0.25rem(4px)与 token-architecture 中示例的 0.5rem 略有差异,应以 primitive-tokens.md 为准——它是本仓库该体系的最终定义来源。

6. 阴影令牌(Shadows)

:root { --shadow-none: none; --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow-default: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); }

阴影采用 Tailwind 同款的两层阴影组合(主阴影 + 偏移方向阴影),透明度使用rgb(0 0 0 / 0.05)现代空格语法。层级用途:sm用于按钮等小元素轻微浮起,default用于卡片默认态,md用于卡片 hover 提升,lg/xl用于弹窗(如组件层--dialog-shadow: var(--shadow-lg)),2xl用于强调浮层。

7. 动效令牌(Motion / Duration)

:root { --duration-75: 75ms; --duration-100: 100ms; --duration-150: 150ms; --duration-200: 200ms; --duration-300: 300ms; --duration-500: 500ms; --duration-700: 700ms; --duration-1000: 1000ms; /* Semantic durations */ --duration-fast: var(--duration-150); --duration-normal: var(--duration-200); --duration-slow: var(--duration-300); }

这是 primitive 文档中唯一的"半语义"定义:底层提供从 75ms 到 1000ms 的完整时间梯度,同时预置三个语义别名(fast/normal/slow)。在组件中应优先使用语义别名,例如按钮过渡transition: background var(--duration-fast)。较长的 500/700/1000ms 用于页面级动画或进度类动效。

8. 层级令牌(Z-Index Scale)

:root { --z-auto: auto; --z-0: 0; --z-10: 10; --z-20: 20; --z-30: 30; --z-40: 40; --z-50: 50; --z-dropdown: 1000; --z-sticky: 1100; --z-modal: 1200; --z-popover: 1300; --z-tooltip: 1400; }

z-index 是最容易被滥用为魔数的令牌族。primitive 层同时提供数值刻度(0–50,步进 10)与语义层级的"安全区"(1000 起每层 +100)。这样设计的原因:数值刻度用于页面内相对层级,而 1000+ 语义档位用于浮层体系,保证 dropdown/sticky/modal/popover/tooltip 之间有稳定的层级差,避免出现z-index: 999999之类的魔数。

9. 命名约定与完整令牌清单

primitive 令牌统一遵循--{category}-{item}-{variant}结构(详见 token-architecture.md 的命名约定章节):

类别命名模式示例
color--color-{色系}-{梯度}--color-gray-100--color-blue-600
space--space-{数值}--space-4--space-2-5
font-size--font-size-{档位}--font-size-sm--font-size-2xl
leading--leading-{风格}--leading-tight--leading-normal
font-weight--font-weight-{档位}--font-weight-semibold
tracking--tracking-{风格}--tracking-wide
radius--radius-{档位}--radius-lg--radius-full
shadow--shadow-{档位}--shadow-md
duration--duration-{毫秒}--duration-200
z--z-{数值/语义}--z-40--z-modal

10. 在工程中落地:从 JSON 到 CSS 再到校验

10.1 用模板 JSON 定义原始令牌

primitive 文档给出的是最终 CSS 形态,而仓库提供的 design-tokens-starter.json 是其上游 JSON 数据源,采用 W3C Design Tokens Community Group 的$value/$type格式。primitive 层在 JSON 中位于顶层primitive键下:

{ "primitive": { "color": { "gray": { "50": { "$value": "#F9FAFB", "$type": "color" } }, "blue": { "600": { "$value": "#2563EB", "$type": "color" } } }, "spacing": { "4": { "$value": "1rem", "$type": "dimension" } }, "fontSize": { "sm": { "$value": "0.875rem", "$type": "dimension" } } } }

语义层通过{primitive.color.blue.600}这样的引用语法指向原始值,组件层再引用语义层。

10.2 生成 CSS

仓库配套脚本 generate-tokens.cjs 将上述 JSON 一键生成 CSS 变量文件:

node scripts/generate-tokens.cjs --config tokens.json -o tokens.css

脚本工作流程(源码可见):

  1. parseArgs()解析-c/--config-o/--output-f/--format(css | tailwind)参数;
  2. resolveReference()递归解析{primitive.color.blue.600}形式的引用链;
  3. flattenTokens()将嵌套 JSON 扁平化为--category-item-variant形式的 CSS 变量;
  4. generateCSS()PRIMITIVES → SEMANTIC → COMPONENTS → DARK MODE四段输出;
  5. generateTailwind()将语义层--color-*变量转换为 Tailwindtheme.extend.colors片段。

--format tailwind可进一步生成 Tailwind 颜色配置:

node scripts/generate-tokens.cjs --config tokens.json --format tailwind

10.3 校验:杜绝硬编码

validate-tokens.cjs 用于扫描代码库,找出本应使用令牌却写成魔数的地方:

node scripts/validate-tokens.cjs --dir src/ node scripts/validate-tokens.cjs --dir src/ --fix # 仅提示建议,不自动改写

它检测四类违规:硬编码 hex 颜色(#RGB/#RRGGBB)、硬编码 RGB 颜色、两位及以上像素值、CSS 中的硬编码 rem 值,并跳过tailwind.configglobals.csstokens.css/json等令牌定义文件。仓库测试 test_validate_tokens.py 验证了关键回归场景:即使一行中同时存在var(--...)引用和硬编码值,硬编码值仍会被正确标记为违规,而纯令牌行不会产生误报。

11. 常见误区与最佳实践

综合 primitive 文档、语义层文档与校验脚本的规则,落地时需注意:

  1. 组件中绝不直接使用 primitive 值.card { background: var(--color-gray-50) }是错误用法,应写为var(--color-card)(语义层)。primitive 只应被语义层/组件层引用,组件消费语义层,这是 semantic-tokens.md 明确强调的规则。
  2. 颜色禁用裸 hex。SKILL.md 的最佳实践明确指出"Never use raw hex in components - always reference tokens"。
  3. 间距/字号一律走 token,从校验脚本的pixelValue/remValue规则可见,除 0 与 1px 外的散落像素值和 rem 值都会被标记。
  4. 语义别名优先:动效使用--duration-fast/normal/slow而非直接写150ms;层级使用--z-modal而非1200
  5. primitive 是"极少变更"层:品牌换色只改 primitive 色值,主题切换只改 semantic 覆盖(.dark { --color-background: var(--color-gray-950); ... }),组件层保持零改动。

12. 相关阅读

  • primitive-tokens.md:本文主题,全部原始令牌定义
  • token-architecture.md:三层令牌架构与命名约定
  • semantic-tokens.md:语义层如何引用 primitive 并支撑暗色主题
  • component-tokens.md:组件层如何消费语义层
  • tailwind-integration.md:将令牌映射到 Tailwind 配置
  • design-tokens-starter.json:三层令牌 JSON 模板
  • generate-tokens.cjs 与 validate-tokens.cjs:生成与校验脚本
  • SKILL.md:设计系统技能总览与最佳实践

【免费下载链接】ui-ux-pro-max-skillAn AI skill that provides design intelligence for building professional UI/UX across multiple platforms.项目地址: https://gitcode.com/gh_mirrors/ui/ui-ux-pro-max-skill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询