名称: 原子设计-夸克 用户可调用: false 描述: 在设计与设计令牌、CSS自定义属性以及构成原子基础以下的原始值时使用。夸克是亚原子构建块。 允许工具:
- Bash
- 读取
- 写入
- 编辑
- Glob
- Grep
原子设计:夸克
掌握创建和组织夸克——构成设计系统基础的亚原子设计令牌和原始值。夸克是原子消费的最小构建块。
什么是夸克?
夸克扩展了布拉德·弗罗斯特的原子设计方法论,添加了原子以下的级别。原子是最小的UI组件,夸克是最小的设计值——构建原子的原材料。
夸克是:
- 原始值:颜色、间距单位、字体大小、阴影
- 非可视化:它们本身不渲染任何内容
- 设计令牌:定义设计语言的命名常量
- 由原子消费:原子导入并使用夸克进行样式设计
夸克 vs 原子
| 方面 | 夸克 | 原子 |
|---|---|---|
| 性质 | 值/令牌 | UI组件 |
| 渲染 | 无(CSS变量、常量) | 可视化元素 |
| 示例 | --color-primary-500、spacing.md |
按钮、输入、标签 |
| 导入 | 无(基础级别) | 仅夸克 |
| 目的 | 定义设计语言 | 实现设计语言 |
常见夸克类型
颜色令牌
// 夸克/颜色.ts
export const colors = {
// 品牌颜色
primary: {
50: '#e3f2fd',
100: '#bbdefb',
200: '#90caf9',
300: '#64b5f6',
400: '#42a5f5',
500: '#2196f3', // 主色
600: '#1e88e5',
700: '#1976d2',
800: '#1565c0',
900: '#0d47a1',
},
// 语义颜色
success: {
light: '#4caf50',
main: '#2e7d32',
dark: '#1b5e20',
},
warning: {
light: '#ff9800',
main: '#ed6c02',
dark: '#e65100',
},
danger: {
light: '#ef5350',
main: '#d32f2f',
dark: '#c62828',
},
// 中性颜色
neutral: {
0: '#ffffff',
50: '#fafafa',
100: '#f5f5f5',
200: '#eeeeee',
300: '#e0e0e0',
400: '#bdbdbd',
500: '#9e9e9e',
600: '#757575',
700: '#616161',
800: '#424242',
900: '#212121',
},
} as const;
export type ColorScale = keyof typeof colors;
export type PrimaryShade = keyof typeof colors.primary;
间距令牌
// 夸克/间距.ts
export const spacing = {
px: '1px',
0: '0',
0.5: '0.125rem', // 2px
1: '0.25rem', // 4px
1.5: '0.375rem', // 6px
2: '0.5rem', // 8px
2.5: '0.625rem', // 10px
3: '0.75rem', // 12px
3.5: '0.875rem', // 14px
4: '1rem', // 16px
5: '1.25rem', // 20px
6: '1.5rem', // 24px
7: '1.75rem', // 28px
8: '2rem', // 32px
9: '2.25rem', // 36px
10: '2.5rem', // 40px
12: '3rem', // 48px
14: '3.5rem', // 56px
16: '4rem', // 64px
20: '5rem', // 80px
24: '6rem', // 96px
} as const;
// 语义间距别名
export const spacingAliases = {
none: spacing[0],
xs: spacing[1],
sm: spacing[2],
md: spacing[4],
lg: spacing[6],
xl: spacing[8],
'2xl': spacing[12],
'3xl': spacing[16],
} as const;
排版令牌
// 夸克/排版.ts
export const fontFamily = {
sans: [
'Inter',
'ui-sans-serif',
'system-ui',
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'sans-serif',
].join(', '),
serif: [
'Georgia',
'Cambria',
'Times New Roman',
'Times',
'serif',
].join(', '),
mono: [
'Fira Code',
'ui-monospace',
'SFMono-Regular',
'Menlo',
'Monaco',
'Consolas',
'monospace',
].join(', '),
} as const;
export const fontSize = {
xs: '0.75rem', // 12px
sm: '0.875rem', // 14px
base: '1rem', // 16px
lg: '1.125rem', // 18px
xl: '1.25rem', // 20px
'2xl': '1.5rem', // 24px
'3xl': '1.875rem', // 30px
'4xl': '2.25rem', // 36px
'5xl': '3rem', // 48px
'6xl': '3.75rem', // 60px
} as const;
export const fontWeight = {
thin: 100,
extralight: 200,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
black: 900,
} as const;
export const lineHeight = {
none: 1,
tight: 1.25,
snug: 1.375,
normal: 1.5,
relaxed: 1.625,
loose: 2,
} as const;
export const letterSpacing = {
tighter: '-0.05em',
tight: '-0.025em',
normal: '0em',
wide: '0.025em',
wider: '0.05em',
widest: '0.1em',
} as const;
边框令牌
// 夸克/边框.ts
export const borderRadius = {
none: '0',
sm: '0.125rem', // 2px
DEFAULT: '0.25rem', // 4px
md: '0.375rem', // 6px
lg: '0.5rem', // 8px
xl: '0.75rem', // 12px
'2xl': '1rem', // 16px
'3xl': '1.5rem', // 24px
full: '9999px',
} as const;
export const borderWidth = {
DEFAULT: '1px',
0: '0',
2: '2px',
4: '4px',
8: '8px',
} as const;
阴影令牌
// 夸克/阴影.ts
export const shadows = {
none: 'none',
sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
'2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
} as const;
// 焦点环阴影
export const focusRings = {
primary: '0 0 0 3px rgba(33, 150, 243, 0.4)',
danger: '0 0 0 3px rgba(239, 83, 80, 0.4)',
success: '0 0 0 3px rgba(76, 175, 80, 0.4)',
} as const;
动画令牌
// 夸克/动画.ts
export const duration = {
instant: '0ms',
fast: '100ms',
normal: '200ms',
slow: '300ms',
slower: '500ms',
} as const;
export const easing = {
linear: 'linear',
in: 'cubic-bezier(0.4, 0, 1, 1)',
out: 'cubic-bezier(0, 0, 0.2, 1)',
inOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
} as const;
export const transitions = {
none: 'none',
all: `all ${duration.normal} ${easing.inOut}`,
colors: `background-color, border-color, color, fill, stroke ${duration.normal} ${easing.inOut}`,
opacity: `opacity ${duration.normal} ${easing.inOut}`,
shadow: `box-shadow ${duration.normal} ${easing.inOut}`,
transform: `transform ${duration.normal} ${easing.inOut}`,
} as const;
断点令牌
// 夸克/断点.ts
export const breakpoints = {
xs: '320px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
} as const;
// 媒体查询助手
export const mediaQueries = {
xs: `@media (min-width: ${breakpoints.xs})`,
sm: `@media (min-width: ${breakpoints.sm})`,
md: `@media (min-width: ${breakpoints.md})`,
lg: `@media (min-width: ${breakpoints.lg})`,
xl: `@media (min-width: ${breakpoints.xl})`,
'2xl': `@media (min-width: ${breakpoints['2xl']})`,
} as const;
Z-索引令牌
// 夸克/z-index.ts
export const zIndex = {
hide: -1,
base: 0,
raised: 1,
dropdown: 1000,
sticky: 1100,
fixed: 1200,
overlay: 1300,
modal: 1400,
popover: 1500,
tooltip: 1600,
toast: 1700,
} as const;
CSS自定义属性
将夸克导出为CSS自定义属性以进行运行时主题化。
// 夸克/css-variables.ts
import { colors, spacing, fontSize, fontFamily } from './index';
export function generateCSSVariables(): string {
const lines: string[] = [':root {'];
// 颜色
Object.entries(colors).forEach(([category, shades]) => {
if (typeof shades === 'object') {
Object.entries(shades).forEach(([shade, value]) => {
lines.push(` --color-${category}-${shade}: ${value};`);
});
}
});
// 间距
Object.entries(spacing).forEach(([key, value]) => {
const sanitizedKey = key.replace('.', '_');
lines.push(` --spacing-${sanitizedKey}: ${value};`);
});
// 排版
Object.entries(fontSize).forEach(([key, value]) => {
lines.push(` --font-size-${key}: ${value};`);
});
Object.entries(fontFamily).forEach(([key, value]) => {
lines.push(` --font-family-${key}: ${value};`);
});
lines.push('}');
return lines.join('
');
}
生成的CSS
/* 夸克/variables.css - 生成输出 */
:root {
/* 颜色 - 主色 */
--color-primary-50: #e3f2fd;
--color-primary-100: #bbdefb;
--color-primary-500: #2196f3;
--color-primary-900: #0d47a1;
/* 颜色 - 中性色 */
--color-neutral-0: #ffffff;
--color-neutral-100: #f5f5f5;
--color-neutral-900: #212121;
/* 间距 */
--spacing-0: 0;
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-4: 1rem;
--spacing-8: 2rem;
/* 排版 */
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-family-sans: Inter, ui-sans-serif, system-ui, sans-serif;
--font-family-mono: Fira Code, ui-monospace, monospace;
/* 边框 */
--border-radius-sm: 0.125rem;
--border-radius-md: 0.375rem;
--border-radius-lg: 0.5rem;
--border-radius-full: 9999px;
/* 阴影 */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
/* 过渡 */
--duration-fast: 100ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
--easing-in-out: cubic-bezier(0.4, 0, 0.2, 1);
}
目录结构
推荐结构
src/
夸克/ # 设计令牌
index.ts # 桶导出
colors.ts
spacing.ts
typography.ts
borders.ts
shadows.ts
animations.ts
breakpoints.ts
z-index.ts
css-variables.ts # CSS自定义属性生成器
variables.css # 生成的CSS文件
组件/
原子/ # 消费夸克的原子
按钮/
输入/
分子/
有机体/
模板/
页面/
索引桶导出
// 夸克/index.ts
export * from './colors';
export * from './spacing';
export * from './typography';
export * from './borders';
export * from './shadows';
export * from './animations';
export * from './breakpoints';
export * from './z-index';
在原子中使用夸克
CSS-in-JS (styled-components/emotion)
// 原子/按钮/按钮.tsx
import styled from 'styled-components';
import { colors, spacing, fontSize, borderRadius, transitions } from '@/夸克';
export const Button = styled.button<{ variant?: 'primary' | 'secondary' }>`
padding: ${spacing[2]} ${spacing[4]};
font-size: ${fontSize.base};
border-radius: ${borderRadius.md};
transition: ${transitions.colors};
${({ variant = 'primary' }) =>
variant === 'primary'
? `
background-color: ${colors.primary[500]};
color: ${colors.neutral[0]};
&:hover {
background-color: ${colors.primary[600]};
}
`
: `
background-color: transparent;
color: ${colors.primary[500]};
border: 1px solid ${colors.primary[500]};
&:hover {
background-color: ${colors.primary[50]};
}
`}
`;
CSS模块与变量
// 原子/按钮/按钮.tsx
import styles from './按钮.module.css';
export const Button = ({ variant = 'primary', children }) => (
<button className={`${styles.button} ${styles[variant]}`}>
{children}
</button>
);
/* 原子/按钮/按钮.module.css */
.button {
padding: var(--spacing-2) var(--spacing-4);
font-size: var(--font-size-base);
border-radius: var(--border-radius-md);
transition: var(--duration-normal) var(--easing-in-out);
}
.primary {
background-color: var(--color-primary-500);
color: var(--color-neutral-0);
}
.primary:hover {
background-color: var(--color-primary-600);
}
.secondary {
background-color: transparent;
color: var(--color-primary-500);
border: 1px solid var(--color-primary-500);
}
Tailwind CSS集成
// tailwind.config.js
const { colors, spacing, fontSize, borderRadius } = require('./src/夸克');
module.exports = {
theme: {
colors,
spacing,
fontSize,
borderRadius,
},
};
使用夸克进行主题化
暗黑模式支持
// 夸克/主题.ts
import { colors } from './colors';
export const lightTheme = {
background: colors.neutral[0],
foreground: colors.neutral[900],
muted: colors.neutral[100],
mutedForeground: colors.neutral[500],
primary: colors.primary[500],
primaryForeground: colors.neutral[0],
};
export const darkTheme = {
background: colors.neutral[900],
foreground: colors.neutral[0],
muted: colors.neutral[800],
mutedForeground: colors.neutral[400],
primary: colors.primary[400],
primaryForeground: colors.neutral[900],
};
export type Theme = typeof lightTheme;
CSS变量主题化
/* 夸克/主题-亮.css */
:root {
--background: var(--color-neutral-0);
--foreground: var(--color-neutral-900);
--muted: var(--color-neutral-100);
--primary: var(--color-primary-500);
}
/* 夸克/主题-暗.css */
[data-theme="dark"] {
--background: var(--color-neutral-900);
--foreground: var(--color-neutral-0);
--muted: var(--color-neutral-800);
--primary: var(--color-primary-400);
}
最佳实践
1. 使用语义名称
// 好: 语义命名
export const colors = {
primary: { ... },
danger: { ... },
success: { ... },
};
// 坏: 原始颜色名称
export const colors = {
blue: { ... },
red: { ... },
green: { ... },
};
2. 一致的尺度
// 好: 一致的尺度模式
export const colors = {
primary: { 50, 100, 200, 300, 400, 500, 600, 700, 800, 900 },
secondary: { 50, 100, 200, 300, 400, 500, 600, 700, 800, 900 },
};
// 坏: 不一致的尺度
export const colors = {
primary: { light, medium, dark },
secondary: { 100, 500, 900 },
};
3. 类型安全
// 好: 类型安全的令牌
export const spacing = { ... } as const;
export type SpacingKey = keyof typeof spacing;
function getSpacing(key: SpacingKey): string {
return spacing[key];
}
// 坏: 非类型化字符串
function getSpacing(key: string): string {
return spacing[key]; // 无类型检查
}
4. 单一来源
// 好: 夸克作为唯一来源
// 所有组件从夸克导入
import { colors } from '@/夸克';
// 坏: 重复值
const Button = styled.button`
color: #2196f3; // 硬编码,非夸克!
`;
5. 文档化
// 好: 文档化您的令牌
/**
* 主色尺度
* 使用 500 表示默认主色操作
* 使用 600+ 表示悬停/活动状态
* 使用 100-400 表示背景/强调
*/
export const primary = { ... };
要避免的反模式
1. 夸克带有逻辑
// 坏: 夸克应为纯值
export const getColor = (isDark: boolean) =>
isDark ? '#ffffff' : '#000000';
// 好: 纯值,逻辑在组件/主题中
export const colors = {
light: { text: '#000000' },
dark: { text: '#ffffff' },
};
2. 组件特定令牌
// 坏: 特定组件的令牌
export const buttonPrimaryBackground = '#2196f3';
// 好: 通用语义令牌
export const colors = {
primary: { 500: '#2196f3' },
};
3. 令牌过多
// 坏: 每个可能值的令牌
export const spacing = {
1: '1px',
2: '2px',
3: '3px',
4: '4px',
5: '5px',
// ... 100 多个值
};
// 好: 深思熟虑、可用的尺度
export const spacing = {
1: '0.25rem',
2: '0.5rem',
4: '1rem',
6: '1.5rem',
8: '2rem',
};
何时使用此技能
- 为新设计系统设置设计令牌
- 将现有CSS变量组织为结构化系统
- 创建可主题化的组件库
- 确保大型代码库的一致性
- 构建白标签或多品牌应用程序
- 从硬编码值迁移到设计令牌
相关技能
原子设计基础- 核心方法论概述原子设计原子- 创建消费夸克的原子组件原子设计集成- 框架特定实现模式
资源
文档
- 设计令牌W3C社区组: https://www.w3.org/community/design-tokens/
- Style Dictionary: https://amzn.github.io/style-dictionary/
- Tokens Studio: https://tokens.studio/
工具
- Figma Tokens: https://www.figma.com/community/plugin/843461159747178978
- Style Dictionary: https://amzn.github.io/style-dictionary/
- Theme UI: https://theme-ui.com/