# 设计变量使用规范 > 基于 `@oppein-react/design-tokens` 的前端样式实现规范。 --- ## 目标 本规范用于统一项目中颜色、间距、字体、圆角、阴影和主题语义变量的使用方式,避免出现以下问题: - 同一项目内同时混用 token、硬编码 HEX 和临时样式值 - 视觉稿已统一,但代码层无法稳定复用设计变量 - 亮暗主题切换时,组件样式无法跟随语义变量变化 - 业务组件直接依赖底层常量,导致样式难以替换和升级 核心原则: 1. 业务样式优先消费 CSS 变量或 UnoCSS token class 2. 语义优先于具体色值,优先写 `var(--op-primary)` 而不是固定品牌阶值 3. 亮暗主题切换统一通过 `.dark` 类名驱动,不在组件内自行维护双份颜色 4. TypeScript token 常量仅用于脚本、配置、图表主题等非 CSS 场景 --- ## 安装与接入 ### 包安装 - 外部项目需要先配置 npm registry,再安装 `@oppein-react/design-tokens` - monorepo 内部 workspace 包,依赖统一写成 `"@oppein-react/design-tokens": "workspace:*"` - 设计变量包是叶子包,不应再反向依赖业务包 ### CSS 注入 - 应用入口必须导入 `@oppein-react/design-tokens/css` - 入口导入只能做一次,避免在页面组件或局部样式文件重复注入 - 推荐放在 `src/main.ts` 或应用级入口文件 示例: ```ts import "@oppein-react/design-tokens/css"; ``` --- ## 样式消费规则 ### 1. CSS 变量 适用场景: - 组件样式文件 - CSS Modules - 全局样式 - 需要跟随暗色主题自动切换的颜色、阴影、背景、边框 规则: - 全局 CSS 变量统一使用 `--op-` 前缀 - 颜色优先使用语义变量,如 `--op-primary`、`--op-background`、`--op-foreground` - 只有在明确需要某个色板阶值时,才使用 `--op-color-brand-6` 这类全局色板变量 - 间距、圆角、阴影统一使用 token 变量,不要自己再写一套近似值 示例: ```css .page { color: var(--op-foreground); background: var(--op-background); } .primary-button { color: var(--op-color-white); background: var(--op-primary); border-radius: var(--op-radius-button); box-shadow: var(--op-shadow-1); padding: var(--op-spacing-xs) var(--op-spacing-sm); } ``` ### 2. UnoCSS token class 适用场景: - 项目已经启用 UnoCSS - 样式以原子类为主,且希望直接映射设计 token 规则: - `uno.config.ts` 中统一启用 `presetOppein()` - 业务代码直接使用 token class,如 `bg-background`、`text-foreground`、`rounded-card` - 不要一边启用 preset,一边继续大面积写无语义的颜色 class 或硬编码 style - 如需覆盖品牌主色,可在 `presetOppein({ primary })` 中配置,不在业务组件中零散覆盖 示例: ```ts import { defineConfig, presetUno } from "unocss"; import { presetOppein } from "@oppein-react/design-tokens/unocss"; export default defineConfig({ presets: [presetUno(), presetOppein()], }); ``` ```tsx export function Example() { return (
); } ``` --- ## 变量选择顺序 新增样式时,按以下顺序选择 token: 1. 先找语义变量 例如:`--op-primary`、`--op-background-secondary`、`--op-border` 2. 再找语义尺寸变量 例如:`--op-spacing-md`、`--op-radius-card`、`--op-shadow-2` 3. 最后才使用全局色板阶值 例如:`--op-color-brand-6` 只有在以下场景允许直接使用色板阶值: - 图表配色 - 特殊插画或装饰性视觉 - 设计稿明确指定阶值,而不是语义角色 --- ## 主题切换规则 - 亮色主题默认挂在 `:root` - 暗色主题统一挂在 `.dark` - 切换主题时,只允许操作根节点的 `dark` 类名 - 禁止在业务组件内通过条件分支手写两套 HEX 颜色实现明暗主题 示例: ```ts document.documentElement.classList.toggle("dark", isDark); ``` --- ## TypeScript 常量使用边界 `@oppein-react/design-tokens` 导出的 `colors`、`spacing`、`radius`、`shadows`、`lightTheme`、`darkTheme` 适用于: - 图表主题 - JS 配置对象 - 运行时计算 - 非 CSS 场景的默认值或映射表 禁止把这些常量当作业务组件样式的默认写法,尤其不要在 JSX 内直接硬塞原始色值: ```ts import { colors, lightTheme, radius, spacing } from "@oppein-react/design-tokens"; const primary = colors.brand[6]; const pageBackground = lightTheme.background; const cardRadius = radius.card; const gap = spacing.md; ``` --- ## 禁止行为 - 在组件、页面、全局样式中硬编码颜色值,如 `#3370FF`、`rgba(0,0,0,.12)`,而对应 token 已存在 - 新增一套项目私有的 `--primary`、`--brand-blue` 之类变量,与 `--op-` 体系并行 - 在组件内部手动维护亮色和暗色两套颜色常量 - CSS 变量已经能表达的场景,仍在 JS 中拼接大量内联样式对象 - 为了局部视觉效果随意创造规范外阴影、圆角和间距值 --- ## Wrong vs Correct ### 颜色 #### Wrong ```css .tab-active { color: #3370ff; border-color: #3370ff; } ``` #### Correct ```css .tab-active { color: var(--op-primary); border-color: var(--op-primary); } ``` ### 圆角与阴影 #### Wrong ```css .card { border-radius: 15px; box-shadow: 0 6px 18px rgba(17, 26, 44, 0.08); } ``` #### Correct ```css .card { border-radius: var(--op-radius-card); box-shadow: var(--op-shadow-2); } ``` ### 主题切换 #### Wrong ```tsx const style = { color: isDark ? "#ffffff" : "#1f2329", background: isDark ? "#141414" : "#ffffff", }; ``` #### Correct ```tsx document.documentElement.classList.toggle("dark", isDark); ``` ```css .panel { color: var(--op-foreground); background: var(--op-background); } ``` --- ## 开发与验收清单 提交前至少确认: - 入口是否已注入 `@oppein-react/design-tokens/css` - 颜色、圆角、阴影、间距是否优先使用了 `--op-` 变量 - 可用语义变量时,是否避免了直接写色板阶值 - 是否避免新增硬编码 HEX、RGB、RGBA - 暗色模式是否通过 `.dark` 驱动,而不是组件局部条件分支 - 项目启用 UnoCSS 时,是否已优先使用 token class 而非重复造 class --- ## 相关规范 - 视觉语义与组件形态:`DESIGN.md` - 目录和样式归属:`frontend-structure-guidelines.md` - 提交前检查:`quality-guidelines.md`