Files

293 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: system/design-tokens
title: Design Tokens System
description: Complete CSS custom properties system for consistent spacing, typography, color, and motion tokens.
tags: [system, design-tokens, css-variables, architecture, consistency]
source: stripe.com, linear.app, raycast.com, design-systems.dev
---
# Design Tokens System
## Что это
Единая система CSS-переменных (custom properties), которая обеспечивает консистентность дизайна. Все размеры, цвета, шрифты, анимации управляются из одного места.
## Архитектура
### File Structure
```
css/
├── tokens/
│ ├── color.css # Цветовая палитра
│ ├── spacing.css # Масштаб отступов
│ ├── typography.css # Масштаб шрифтов
│ ├── motion.css # Параметры анимаций
│ └── layout.css # Breakpoints, container sizes
├── components/ # Компоненты используют tokens
└── pages/ # Страницы используют components
```
### CSS Custom Properties
```css
/* tokens/color.css */
:root {
/* Brand colors */
--color-brand-50: #f0f5ff;
--color-brand-100: #e0eaff;
--color-brand-200: #b9d0ff;
--color-brand-400: #4f8fff;
--color-brand-500: #2563eb;
--color-brand-600: #1d4ed8;
--color-brand-700: #1e40af;
/* Semantic colors */
--color-bg: #0a0a0a;
--color-surface: #111111;
--color-surface-hover: #1a1a1a;
--color-border: rgba(255,255,255,0.06);
--color-border-hover: rgba(255,255,255,0.12);
/* Text colors */
--color-text-primary: #f5f5f5;
--color-text-secondary: #a0a0a0;
--color-text-muted: #666666;
/* Accent colors */
--color-accent: #6366f1;
--color-accent-hover: #818cf8;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
}
```
```css
/* tokens/spacing.css */
:root {
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-16: 64px;
--space-20: 80px;
--space-24: 96px;
--space-32: 128px;
}
```
```css
/* tokens/typography.css */
:root {
--font-primary: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
/* Scale ratio: 1.25 (Major Third) */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
--text-4xl: 2.25rem;
--text-5xl: 3rem;
--text-6xl: 3.75rem;
--text-7xl: 4.5rem;
/* Line heights */
--leading-tight: 1.1;
--leading-snug: 1.3;
--leading-normal: 1.6;
--leading-relaxed: 1.75;
}
```
```css
/* tokens/motion.css */
:root {
--ease-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
--duration-fast: 150ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
--duration-xslow: 800ms;
}
```
```css
/* tokens/layout.css */
:root {
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1200px;
--container-2xl: 1400px;
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
}
```
## CSS Reset с токенами
```css
/* css/reset.css */
*, *::before, *::after {
box-sizing: border-box;
}
html {
font-size: 100%; /* 16px */
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-primary);
background: var(--color-bg);
color: var(--color-text-primary);
line-height: var(--leading-normal);
overflow-x: hidden;
}
/* Typography with tokens */
h1 { font-size: var(--text-5xl); line-height: var(--leading-tight); font-weight: 800; }
h2 { font-size: var(--text-4xl); line-height: var(--leading-tight); font-weight: 700; }
h3 { font-size: var(--text-2xl); line-height: var(--leading-snug); font-weight: 600; }
/* Spacing with tokens */
.p-4 { padding: var(--space-4); }
.p-6 { padding: var(--space-6); }
.p-8 { padding: var(--space-8); }
.gap-4 { gap: var(--space-4); }
.gap-6 { gap: var(--space-6); }
```
## Component Tokens
```css
/* tokens/components.css */
:root {
/* Buttons */
--btn-radius: 10px;
--btn-padding-y: var(--space-3);
--btn-padding-x: var(--space-6);
--btn-font-weight: 600;
--btn-font-size: var(--text-base);
/* Cards */
--card-radius: 16px;
--card-padding: var(--space-8);
--card-border: 1px solid var(--color-border);
--card-bg: var(--color-surface);
--card-hover-shadow: 0 4px 20px rgba(0,0,0,0.3);
/* Inputs */
--input-radius: 8px;
--input-padding: var(--space-3) var(--space-4);
--input-border: 1px solid var(--color-border);
--input-focus-border: 1px solid var(--color-brand-500);
}
```
## Theming with Tokens
### Light Theme
```css
:root {
--color-bg: #ffffff;
--color-surface: #f5f5f5;
--color-text-primary: #0a0a0a;
--color-text-secondary: #555555;
--color-border: rgba(0,0,0,0.08);
}
[data-theme="light"] {
--color-bg: #ffffff;
--color-surface: #f5f5f5;
--color-text-primary: #0a0a0a;
}
[data-theme="dark"] {
--color-bg: #0a0a0a;
--color-surface: #111111;
--color-text-primary: #f5f5f5;
}
```
### Dark Theme (default)
```css
:root {
--color-bg: #0a0a0a;
--color-surface: #111111;
--color-text-primary: #f5f5f5;
}
```
## Utility Classes
```css
/* tokens/utilities.css */
.text-primary { color: var(--color-text-primary); }
.text-secondary { color: var(--color-text-secondary); }
.text-muted { color: var(--color-text-muted); }
.bg-surface { background: var(--color-surface); }
.border-default { border: var(--color-border); }
.border-hover:hover { border-color: var(--color-border-hover); }
.rounded-sm { border-radius: 4px; }
.rounded { border-radius: 8px; }
.rounded-md { border-radius: 12px; }
.rounded-lg { border-radius: 16px; }
.rounded-xl { border-radius: 24px; }
.rounded-full { border-radius: 9999px; }
/* Container */
.container {
max-width: var(--container-xl);
margin: 0 auto;
padding: 0 var(--space-6);
}
```
## Anti-patterns
| ❌ Плохо | ✅ Хорошо |
|----------|-----------|
| `padding: 23px` | `padding: var(--space-6)` (24px) |
| `color: #f0f0f0` | `color: var(--color-text-primary)` |
| `font-size: 32px` | `font-size: var(--text-4xl)` |
| `border-radius: 15px` | `border-radius: var(--card-radius)` |
| `transition: 0.2s` | `transition: var(--duration-normal) var(--ease-out)` |
## Best Practices
1. **Один источник истины** — все токены в `tokens/`
2. **Называйте по значению** — `--spacing-md`, `--color-primary`, не `--padding-1`, `--brand-1`
3. **Без hardcoded значений в компонентах** — компоненты только токены
4. **Планируйте на 20% вперёд** — если сейчас нужно 5 размеров spacing, добавьте ещё 2
5. **Dokumentiruyte** — каждый токен должен быть описан
```css
/* ✅ Хороший токен */
:root {
/** Отступ между карточками в сетке */
--card-gap: 24px;
/** Максимальная ширина контента */
--max-width: 1200px;
}
```