@uncinq/design-tokens 1.7.3 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -441
- package/dist/tokens.json +4629 -0
- package/docs/_index.md +119 -0
- package/docs/colors.md +219 -0
- package/docs/customizing.md +78 -0
- package/docs/dark-mode.md +97 -0
- package/docs/dtcg.md +232 -0
- package/docs/naming.md +128 -0
- package/docs/reference.md +129 -0
- package/docs/style-dictionary.md +182 -0
- package/docs/utopia.md +55 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,488 +4,99 @@
|
|
|
4
4
|
|
|
5
5
|
<img width="1280" height="640" alt="share-design-tokens" src="https://github.com/user-attachments/assets/66b8ce73-b07d-4cd0-bcb7-f8e0f7a5bb98" />
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Primitive and semantic design tokens, authored in [DTCG](https://tr.designtokens.org/format/) JSON and compiled to CSS custom properties by [Style Dictionary](https://styledictionary.com/).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Token architecture
|
|
12
|
-
|
|
13
|
-
This package follows the [DTCG](docs/DTCG.md) three-layer model — primitive → semantic → component:
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
primitive → semantic → component
|
|
17
|
-
(raw values) (purpose) (component-scoped, not in this package)
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### Layer 1 — Primitive
|
|
21
|
-
|
|
22
|
-
Raw, context-free values. No opinions about where they're used.
|
|
23
|
-
|
|
24
|
-
```css
|
|
25
|
-
--color-indigo-600: #4338ca;
|
|
26
|
-
--size-16: 1rem;
|
|
27
|
-
--font-weight-bold: 700;
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
A primitive token answers: **"what is the value?"**
|
|
31
|
-
|
|
32
|
-
### Layer 2 — Semantic
|
|
33
|
-
|
|
34
|
-
Named by purpose, not by appearance. References primitives via CSS `var()`.
|
|
35
|
-
|
|
36
|
-
```css
|
|
37
|
-
--color-brand: var(--color-indigo-600);
|
|
38
|
-
--spacing-md: var(--size-30);
|
|
39
|
-
--font-weight-heading: var(--font-weight-bold);
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
A semantic token answers: **"what is this value for?"**
|
|
43
|
-
|
|
44
|
-
This is the layer that gives portability: all projects consuming `@uncinq/design-tokens` share the same semantic API. When the brand color changes, you update one primitive — all semantic tokens that reference it update automatically.
|
|
45
|
-
|
|
46
|
-
### Layer 3 — Component
|
|
47
|
-
|
|
48
|
-
Scoped to a specific component. Generic component tokens are provided by [@uncinq/component-tokens](https://github.com/uncinq/component-tokens); project-specific ones live in each project's own design system.
|
|
49
|
-
|
|
50
|
-
```css
|
|
51
|
-
--alert-border-radius: var(--radius-none);
|
|
52
|
-
--btn-padding-inline: var(--spacing-control);
|
|
53
|
-
--btn-padding-block: var(--spacing-control);
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## Naming convention
|
|
59
|
-
|
|
60
|
-
### Semantic tokens
|
|
61
|
-
|
|
62
|
-
Global semantic tokens follow: `--{category}-{subcategory?}-{variant}-{state?}`
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
--{category} --color
|
|
66
|
-
-{subcategory} --color-text
|
|
67
|
-
-{variant} --color-text-muted
|
|
68
|
-
-{state} --color-text-disabled
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Component tokens
|
|
72
|
-
|
|
73
|
-
Component tokens follow: `--{component}-{property}-{sub-property?}-{state?}`
|
|
74
|
-
|
|
75
|
-
The property mirrors the CSS property name, so the token reads the same way as the CSS declaration it controls — colors excepted, where `color` leads and the role follows (see the rules below).
|
|
9
|
+
## Installation
|
|
76
10
|
|
|
11
|
+
```bash
|
|
12
|
+
npm install @uncinq/design-tokens
|
|
77
13
|
```
|
|
78
|
-
--{component} --btn
|
|
79
|
-
-{property} --btn-padding-inline
|
|
80
|
-
-{sub-property} --btn-color-text-decoration (color + text-decoration)
|
|
81
|
-
-{state} --btn-color-background-hover
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Rules
|
|
85
|
-
|
|
86
|
-
- **Lowercase kebab-case** — always
|
|
87
|
-
- **No component names** in primitive or semantic tokens (`--button-*` belongs in component tokens, not here)
|
|
88
|
-
- **Semantic tokens are named by intent** — they may reference a primitive via `var()` or carry a raw value when the value itself has design intent (e.g. `--z-index-modal: 400`, `--radius-pill: 9999px`)
|
|
89
|
-
- **`color-[role]` for all color tokens** — `color` is the category prefix, the UI role follows: `color-background`, `color-border`, `color-text`, `color-accent`, `color-placeholder`. This groups all color tokens alphabetically under `color-*` and mirrors the structure of global semantic tokens (`--color-background` → `--btn-color-background`). `background` is never abbreviated: `color-background` not `color-bg`.
|
|
90
|
-
- **States at the end** — `-hover`, `-focus`, `-active`, `-disabled`, `-checked`
|
|
91
|
-
- **Alphabetical order** — tokens within a file are sorted alphabetically within each group; group related tokens with a comment when the file has many entries:
|
|
92
|
-
|
|
93
|
-
| Token | Role | CSS property |
|
|
94
|
-
| --- | --- | --- |
|
|
95
|
-
| `--btn-color-background` | background | `background-color` |
|
|
96
|
-
| `--btn-color-border` | border | `border-color` |
|
|
97
|
-
| `--btn-color-text` | text | `color` |
|
|
98
|
-
| `--btn-color-text-decoration` | text-decoration | `text-decoration-color` |
|
|
99
|
-
| `--form-color-accent` | accent | `color` |
|
|
100
|
-
| `--input-color-placeholder` | placeholder | `color` |
|
|
101
14
|
|
|
102
15
|
```css
|
|
103
|
-
/*
|
|
104
|
-
|
|
105
|
-
--color-brand-hover: var(--color-indigo-700);
|
|
106
|
-
|
|
107
|
-
/* Text */
|
|
108
|
-
--color-text: var(--color-gray-900);
|
|
109
|
-
--color-text-muted: var(--color-gray-500);
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Scales
|
|
113
|
-
|
|
114
|
-
| Use case | Scale | Example |
|
|
115
|
-
| --- | --- | --- |
|
|
116
|
-
| Color palettes | Numeric `100–900` | `--color-gray-500` |
|
|
117
|
-
| Heading levels | Zero-padded `01–06` | `--font-size-heading-01` |
|
|
118
|
-
| Layout / spacing | T-shirt `2xs xs sm md lg xl 2xl` | `--spacing-md` |
|
|
119
|
-
| Radius, shadow, size | T-shirt `2xs xs sm md lg xl 2xl` | `--radius-sm` |
|
|
120
|
-
| Purposeful aliases | Named | `--radius-control`, `--radius-pill` |
|
|
121
|
-
|
|
122
|
-
### Category reference
|
|
123
|
-
|
|
124
|
-
| Category | Covers | Example tokens |
|
|
125
|
-
| --- | --- | --- |
|
|
126
|
-
| `color` | All color values | `--color-brand`, `--color-background-muted`, `--color-text-on-dark` |
|
|
127
|
-
| `font-family` | Typefaces | `--font-family-sans`, `--font-family-heading` |
|
|
128
|
-
| `font-size` | Text sizes | `--font-size-sm`, `--font-size-heading-01` |
|
|
129
|
-
| `font-weight` | Weight values | `--font-weight-bold`, `--font-weight-heading` |
|
|
130
|
-
| `line-height` | Line heights | `--line-height-tight`, `--line-height-heading` |
|
|
131
|
-
| `letter-spacing` | Tracking | `--letter-spacing-none`, `--letter-spacing-md` |
|
|
132
|
-
| `text-decoration` | Decoration props | `--text-decoration-offset` |
|
|
133
|
-
| `spacing` | Margin / padding | `--spacing-md`, `--spacing-section` |
|
|
134
|
-
| `size` | Width / height | `--size-16`, `--size-tablet` |
|
|
135
|
-
| `radius` | Border radius | `--radius-md`, `--radius-pill` |
|
|
136
|
-
| `border` | Border style/width | `--border-width-sm`, `--border-style-normal` |
|
|
137
|
-
| `shadow` | Box shadows | `--shadow-md`, `--shadow-center-sm` |
|
|
138
|
-
| `gradient` | Overlay gradients | `--gradient-darken-color-from`, `--gradient-darken-stop` |
|
|
139
|
-
| `duration` | Animation timing | `--duration-fast` |
|
|
140
|
-
| `easing` | Timing functions | `--easing-bounce` |
|
|
141
|
-
| `transition` | Shorthand transitions | `--transition-normal`, `--transition-color` |
|
|
142
|
-
| `ratio` | Aspect ratios | `--ratio-video` |
|
|
143
|
-
| `font-size-fluid` | Responsive fluid type scale (Utopia) | `--font-size-fluid-sm`, `--font-size-fluid-xl` |
|
|
144
|
-
| `spacing-fluid` | Responsive fluid spacing scale (Utopia) | `--spacing-fluid-sm`, `--spacing-fluid-lg` |
|
|
145
|
-
| `focus` | Focus ring tokens | `--focus-color`, `--focus-outline-width` |
|
|
146
|
-
| `opacity` | Opacity values | `--opacity-disabled`, `--opacity-overlay` |
|
|
147
|
-
| `span` | Grid column spans | `--span-full`, `--span-half` |
|
|
148
|
-
| `z-index` | Stacking order | `--z-index-modal`, `--z-index-dropdown` |
|
|
149
|
-
| `max-width` | Readability caps | `--max-width-paragraph` |
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## Colors
|
|
154
|
-
|
|
155
|
-
### Color space — OKLCH
|
|
156
|
-
|
|
157
|
-
All primitive color values are defined in **OKLCH** (`oklch(L C H)`):
|
|
158
|
-
|
|
159
|
-
| Channel | Range | Meaning |
|
|
160
|
-
|---------|-------|---------|
|
|
161
|
-
| `L` | `0 → 1` | Perceptual lightness (0 = black, 1 = white) |
|
|
162
|
-
| `C` | `0 → ~0.4` | Chroma / colorfulness (0 = gray) |
|
|
163
|
-
| `H` | `0° → 360°` | Hue angle |
|
|
164
|
-
|
|
165
|
-
**Why OKLCH over hex/HSL?**
|
|
166
|
-
|
|
167
|
-
- **Perceptually uniform** — equal steps in L produce equal perceived brightness differences, regardless of hue. HSL does not guarantee this (`hsl(60, 100%, 50%)` yellow looks far brighter than `hsl(240, 100%, 50%)` blue at the same L).
|
|
168
|
-
- **Predictable contrast** — you can reason about WCAG contrast by comparing L values without converting to relative luminance.
|
|
169
|
-
- **Better interpolation** — gradients and animations between two OKLCH colors don't pass through muddy grays.
|
|
170
|
-
- **Future-proof** — native in all modern browsers, the color space used by Tailwind v4, Radix, and the W3C Design Tokens spec.
|
|
171
|
-
|
|
172
|
-
> Browser support: Chrome 111+, Firefox 113+, Safari 15.4+. No sRGB fallback is generated — every value ships as `oklch()`, so anything older needs a fallback of its own.
|
|
173
|
-
|
|
174
|
-
### Primitive palette
|
|
175
|
-
|
|
176
|
-
The primitive palette provides **11 steps per hue** (50 → 950), named numerically. These are raw values with no opinion about usage.
|
|
177
|
-
|
|
178
|
-
| Hue | H angle | Character |
|
|
179
|
-
|-----|---------|-----------|
|
|
180
|
-
| `amber` | ≈ 70° | Golden yellow-orange |
|
|
181
|
-
| `blue` | ≈ 260° | Classic blue |
|
|
182
|
-
| `cyan` | ≈ 215° | Bright cyan |
|
|
183
|
-
| `gray` | neutral | Cool neutral |
|
|
184
|
-
| `green` | ≈ 150° | Lush green |
|
|
185
|
-
| `indigo` | ≈ 277° | Blue-violet |
|
|
186
|
-
| `lime` | ≈ 131° | Electric yellow-green |
|
|
187
|
-
| `orange` | ≈ 48° | Vivid orange |
|
|
188
|
-
| `pink` | ≈ 354° | Bright pink |
|
|
189
|
-
| `purple` | ≈ 304° | Rich purple |
|
|
190
|
-
| `red` | ≈ 25° | Classic red |
|
|
191
|
-
| `rose` | ≈ 16° | Pink-red |
|
|
192
|
-
| `sienna` | ≈ 23° | Brick-red (crimson × terracotta) |
|
|
193
|
-
| `sky` | ≈ 237° | Soft sky blue |
|
|
194
|
-
| `teal` | ≈ 183° | Blue-green |
|
|
195
|
-
| `violet` | ≈ 293° | Modern violet |
|
|
196
|
-
| `yellow` | ≈ 86° | Pure yellow |
|
|
197
|
-
|
|
198
|
-
Plus `--color-black` and `--color-white`.
|
|
199
|
-
|
|
200
|
-
**Step guide:**
|
|
201
|
-
|
|
202
|
-
| Step | L (avg) | L amber/yellow/lime | Typical use |
|
|
203
|
-
|------|---------|---------------------|-------------|
|
|
204
|
-
| 50 | ≈ 0.97 | ≈ 0.98 | Page tinted backgrounds, hover states on white |
|
|
205
|
-
| 100 | ≈ 0.94 | ≈ 0.96 | Muted backgrounds, badges, tags |
|
|
206
|
-
| 200 | ≈ 0.90 | ≈ 0.93 | Borders, dividers |
|
|
207
|
-
| 300 | ≈ 0.83 | ≈ 0.88 | Disabled elements, placeholder text |
|
|
208
|
-
| 400 | ≈ 0.72 | ≈ 0.83 | Secondary icons, decorative |
|
|
209
|
-
| 500 | ≈ 0.63 | ≈ 0.77 | Mid-tone — use with dark text for UI |
|
|
210
|
-
| 600 | ≈ 0.53 | ≈ 0.67 | **Default brand/status bg** — white text passes WCAG AA (UI) |
|
|
211
|
-
| 700 | ≈ 0.46 | ≈ 0.55 | Hover state, colored text on white background |
|
|
212
|
-
| 800 | ≈ 0.39 | ≈ 0.47 | Deep accents, high-contrast text |
|
|
213
|
-
| 900 | ≈ 0.33 | ≈ 0.41 | Near-dark, very high contrast |
|
|
214
|
-
| 950 | ≈ 0.22 | ≈ 0.28 | Darkest tint, almost black |
|
|
215
|
-
|
|
216
|
-
> **Note on OKLCH** — L is perceptually uniform, but intrinsically bright hues (amber, yellow, lime) have naturally higher L values at steps 400–700. This is expected behavior, not a calibration error. Gray goes the other way (chroma ≈ 0, no brightness boost, L slightly lower). The "L avg" column is representative of cool chromatic hues (blue, red, green, violet…).
|
|
217
|
-
|
|
218
|
-
### Semantic color tokens
|
|
219
|
-
|
|
220
|
-
Semantic tokens are named by **purpose**, not by value. They reference primitives via `var()`.
|
|
221
|
-
|
|
222
|
-
#### Brand & accent
|
|
16
|
+
/* everything: primitive + semantic + dark theme */
|
|
17
|
+
@import '@uncinq/design-tokens';
|
|
223
18
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
--color-brand-hover: /* hover state of brand */
|
|
228
|
-
--color-brand-strong: /* darkest brand shade */
|
|
19
|
+
/* or by layer */
|
|
20
|
+
@import '@uncinq/design-tokens/css/primitive.css';
|
|
21
|
+
@import '@uncinq/design-tokens/css/semantic.css';
|
|
229
22
|
|
|
230
|
-
|
|
23
|
+
/* or file by file */
|
|
24
|
+
@import '@uncinq/design-tokens/css/semantic/color.css';
|
|
231
25
|
```
|
|
232
26
|
|
|
233
|
-
|
|
27
|
+
Without a build step:
|
|
234
28
|
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
:root {
|
|
238
|
-
--color-brand: var(--color-violet-600);
|
|
239
|
-
--color-brand-muted: var(--color-violet-100);
|
|
240
|
-
--color-brand-hover: var(--color-violet-700);
|
|
241
|
-
--color-brand-strong: var(--color-violet-900);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
29
|
+
```html
|
|
30
|
+
<link rel="stylesheet" href="https://unpkg.com/@uncinq/design-tokens">
|
|
244
31
|
```
|
|
245
32
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
| Token | Default | Usage |
|
|
249
|
-
|-------|---------|-------|
|
|
250
|
-
| `--color-background` | white | Page background |
|
|
251
|
-
| `--color-background-muted` | gray-100 | Subtle section backgrounds |
|
|
252
|
-
| `--color-background-surface` | = `--color-background` | Card / panel backgrounds |
|
|
253
|
-
| `--color-background-media` | gray-200 | Image placeholders, skeleton loaders |
|
|
254
|
-
| `--color-background-accent` | = `--color-accent` | Highlighted sections |
|
|
255
|
-
|
|
256
|
-
#### Text
|
|
257
|
-
|
|
258
|
-
| Token | Default | Usage |
|
|
259
|
-
|-------|---------|-------|
|
|
260
|
-
| `--color-text` | gray-900 | Body text |
|
|
261
|
-
| `--color-text-muted` | gray-500 | Secondary, captions |
|
|
262
|
-
| `--color-text-disabled` | gray-300 | Disabled UI |
|
|
263
|
-
| `--color-heading` | black | Headings |
|
|
264
|
-
| `--color-link` | = `--color-text` | Default link color |
|
|
265
|
-
| `--color-link-hover` | = `--color-accent` | Link hover |
|
|
266
|
-
| `--color-active` | = `--color-accent` | Active nav item |
|
|
267
|
-
| `--color-credit` | = `--color-text-muted` | Bylines, captions |
|
|
268
|
-
|
|
269
|
-
#### Text on colored backgrounds
|
|
33
|
+
Every generated file declares `@layer tokens` itself. Declaring the full layer order is the consuming project's job, and it must happen before any import.
|
|
270
34
|
|
|
271
|
-
|
|
35
|
+
## Token architecture
|
|
272
36
|
|
|
273
|
-
```css
|
|
274
|
-
--color-text-on-brand /* white */
|
|
275
|
-
--color-text-on-accent /* white */
|
|
276
|
-
--color-text-on-dark /* white */
|
|
277
|
-
--color-text-on-light /* gray-900 */
|
|
278
|
-
--color-text-on-surface /* = --color-text */
|
|
279
|
-
--color-text-on-danger /* white */
|
|
280
|
-
--color-text-on-info /* white */
|
|
281
|
-
--color-text-on-success /* white */
|
|
282
|
-
--color-text-on-warning /* gray-900 — amber is bright, dark text required */
|
|
283
37
|
```
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
| Token | Primitive | Notes |
|
|
288
|
-
|-------|-----------|-------|
|
|
289
|
-
| `--color-danger` | red-600 | Errors, destructive actions |
|
|
290
|
-
| `--color-success` | green-600 | Confirmations |
|
|
291
|
-
| `--color-warning` | amber-500 | Warnings — use with `--color-text-on-warning` |
|
|
292
|
-
| `--color-info` | blue-600 | Informational |
|
|
293
|
-
| `--color-dark` | gray-900 | Dark surfaces |
|
|
294
|
-
| `--color-light` | gray-200 | Light surfaces |
|
|
295
|
-
|
|
296
|
-
Each variant has `-muted` (tinted bg) and `-strong` (hover / emphasis) companions:
|
|
297
|
-
|
|
298
|
-
```css
|
|
299
|
-
--color-danger-muted: var(--color-red-100);
|
|
300
|
-
--color-danger-strong: var(--color-red-800); /* used for hover */
|
|
38
|
+
primitive → semantic → component
|
|
39
|
+
(raw values) (purpose) (@uncinq/component-tokens)
|
|
301
40
|
```
|
|
302
41
|
|
|
303
|
-
### Accessibility (WCAG)
|
|
304
|
-
|
|
305
|
-
| Ratio | Requirement |
|
|
306
|
-
|-------|-------------|
|
|
307
|
-
| **4.5 : 1** | Normal text (< 18px / non-bold < 14px) — WCAG AA |
|
|
308
|
-
| **3 : 1** | Large text, UI components (buttons, inputs, icons) — WCAG AA |
|
|
309
|
-
| **7 : 1** | Any text — WCAG AAA |
|
|
310
|
-
|
|
311
|
-
**Rules of thumb for this palette:**
|
|
312
|
-
|
|
313
|
-
- **White text on a colored background** — use step **600 or darker**. Steps 500 and below are typically too light (3–3.5 : 1 ratio).
|
|
314
|
-
- **Colored text on white** — use step **700 or darker** for normal text.
|
|
315
|
-
- **Warning (amber-500)** — always pair with `--color-text-on-warning` (gray-900). Never white text on amber-500.
|
|
316
|
-
- **Decorative only** — any step is fine when color carries no information (icons, borders, illustrations).
|
|
317
|
-
|
|
318
|
-
### Dark theme
|
|
319
|
-
|
|
320
|
-
`tokens/themes/dark.json` re-declares 13 semantic color tokens — backgrounds, border, text, `--form-color-background`, and `--color-shadow`, which flips to white. The generated `dist/css/themes/dark.css` wraps them, still inside `@layer tokens`:
|
|
321
|
-
|
|
322
42
|
```css
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
So the dark scheme follows the OS setting, and `data-color-scheme="light"` on `<html>` opts a page out of it. There is no forced-dark selector — the package never turns dark on a light OS. The theme comes with the full `index.css`; importing `css/semantic.css` alone leaves it out.
|
|
329
|
-
|
|
330
|
-
### Adding a custom hue
|
|
331
|
-
|
|
332
|
-
Add a new primitive scale in `tokens/primitive/color.json`, following the existing pattern — one DTCG color object per step:
|
|
333
|
-
|
|
334
|
-
```json
|
|
335
|
-
{
|
|
336
|
-
"color": {
|
|
337
|
-
"coral": {
|
|
338
|
-
"50": { "$value": { "colorSpace": "oklch", "components": [0.975, 0.014, 35.0] }, "$type": "color" },
|
|
339
|
-
"100": { "$value": { "colorSpace": "oklch", "components": [0.948, 0.032, 35.0] }, "$type": "color" },
|
|
340
|
-
"950": { "$value": { "colorSpace": "oklch", "components": [0.225, 0.078, 35.0] }, "$type": "color" }
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
43
|
+
--color-sienna-600: oklch(0.53 0.195 22); /* primitive: what is the value? */
|
|
44
|
+
--color-brand: var(--color-sienna-600); /* semantic: what is it for? */
|
|
45
|
+
--btn-color-background: var(--color-brand); /* component: where is it used? */
|
|
344
46
|
```
|
|
345
47
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## Customization
|
|
351
|
-
|
|
352
|
-
Two approaches are available depending on how deep the override needs to go.
|
|
353
|
-
|
|
354
|
-
### 1 — CSS override (recommended)
|
|
355
|
-
|
|
356
|
-
All tokens live in `@layer tokens`, the lowest-priority layer in the stack. Any `@layer tokens` block imported **after** this package wins by source order — no specificity tricks needed.
|
|
48
|
+
Overriding the semantic layer is how a project rebrands without forking:
|
|
357
49
|
|
|
358
50
|
```css
|
|
359
51
|
@import '@uncinq/design-tokens';
|
|
360
52
|
|
|
361
53
|
@layer tokens {
|
|
362
54
|
:root {
|
|
363
|
-
--color-brand:
|
|
364
|
-
--color-brand-
|
|
365
|
-
--color-brand-hover: var(--color-violet-700);
|
|
366
|
-
--color-brand-strong: var(--color-violet-900);
|
|
367
|
-
--font-family-sans: 'Inter', system-ui, sans-serif;
|
|
55
|
+
--color-brand: var(--color-violet-600);
|
|
56
|
+
--color-brand-hover: var(--color-violet-700);
|
|
368
57
|
}
|
|
369
58
|
}
|
|
370
59
|
```
|
|
371
60
|
|
|
372
|
-
|
|
61
|
+
## At a glance
|
|
373
62
|
|
|
374
|
-
|
|
63
|
+
- **19 hues** at 11 steps each, all in OKLCH, plus black and white
|
|
64
|
+
- **661 tokens** across 23 source files
|
|
65
|
+
- **CSS output only**, because custom properties are the one format that works unchanged everywhere
|
|
66
|
+
- **Dark theme** as a 13-token overlay, following the OS setting
|
|
375
67
|
|
|
376
|
-
|
|
68
|
+
## Documentation
|
|
377
69
|
|
|
378
|
-
|
|
70
|
+
Full documentation: **[socle.uncinq.dev/docs/design-tokens/](https://socle.uncinq.dev/docs/design-tokens/)**
|
|
379
71
|
|
|
380
|
-
|
|
381
|
-
// style-dictionary.config.js (in the consuming project)
|
|
382
|
-
export default {
|
|
383
|
-
source: [
|
|
384
|
-
'node_modules/@uncinq/design-tokens/tokens/**/*.json',
|
|
385
|
-
'tokens/**/*.json', // project tokens — extend or override the package
|
|
386
|
-
],
|
|
387
|
-
// ...
|
|
388
|
-
};
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
Project token files that define the same path as a package token will override it during the build. New paths are additive.
|
|
392
|
-
|
|
393
|
-
Use this approach to:
|
|
72
|
+
It is also versioned with the code in [`docs/`](docs/), and ships inside the npm package, so it is readable offline and from `node_modules`:
|
|
394
73
|
|
|
395
|
-
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
74
|
+
- [Naming](docs/naming.md) — the grammar, the rules, the scales, the category list
|
|
75
|
+
- [Colors](docs/colors.md) — OKLCH, the palette, the semantic roles, WCAG guidance
|
|
76
|
+
- [Dark mode](docs/dark-mode.md) — the overlay model and how to opt out
|
|
77
|
+
- [Customizing](docs/customizing.md) — the two override strategies
|
|
78
|
+
- [Reference](docs/reference.md) — every token, generated from the sources
|
|
79
|
+
- [DTCG format](docs/dtcg.md) — the authoring format
|
|
80
|
+
- [Style Dictionary](docs/style-dictionary.md) — the build pipeline
|
|
81
|
+
- [Fluid scales](docs/utopia.md) — the Utopia method
|
|
398
82
|
|
|
399
|
-
|
|
400
|
-
| --- | --- |
|
|
401
|
-
| Change brand, typography, a few tokens | CSS `@layer tokens` |
|
|
402
|
-
| Redefine the entire primitive palette | JSON + build |
|
|
403
|
-
| Add project-specific tokens | JSON + build (or CSS if few) |
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Installation
|
|
83
|
+
## Build
|
|
408
84
|
|
|
409
85
|
```bash
|
|
410
|
-
npm install
|
|
411
|
-
#
|
|
412
|
-
yarn add @uncinq/design-tokens
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
### Usage — CSS import
|
|
416
|
-
|
|
417
|
-
```css
|
|
418
|
-
/* everything */
|
|
419
|
-
@import '@uncinq/design-tokens';
|
|
420
|
-
|
|
421
|
-
/* or by layer */
|
|
422
|
-
@import '@uncinq/design-tokens/css/primitive.css';
|
|
423
|
-
@import '@uncinq/design-tokens/css/semantic.css';
|
|
424
|
-
|
|
425
|
-
/* or file by file */
|
|
426
|
-
@import '@uncinq/design-tokens/css/primitive/color.css';
|
|
427
|
-
@import '@uncinq/design-tokens/css/semantic/color.css';
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### Usage — CDN (no build step)
|
|
431
|
-
|
|
432
|
-
```html
|
|
433
|
-
<link rel="stylesheet" href="https://unpkg.com/@uncinq/design-tokens">
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
---
|
|
437
|
-
|
|
438
|
-
## File structure
|
|
439
|
-
|
|
440
|
-
JSON source files (DTCG format — do not edit `dist/`):
|
|
441
|
-
|
|
442
|
-
```
|
|
443
|
-
tokens/
|
|
444
|
-
primitive/
|
|
445
|
-
blur.json ← blur scale (xs → xl)
|
|
446
|
-
color.json ← full color palette (OKLCH, 11 steps per hue)
|
|
447
|
-
font.json ← font families, weights, sizes, line-heights
|
|
448
|
-
shadow.json ← box-shadow scale
|
|
449
|
-
size.json ← rem scale (--size-1 → --size-1920)
|
|
450
|
-
semantic/
|
|
451
|
-
blur.json ← purposeful blur aliases
|
|
452
|
-
border.json ← border styles and widths
|
|
453
|
-
color.json ← purposeful color aliases (--color-brand, --color-background…)
|
|
454
|
-
focus.json ← focus ring tokens (color, style, width, offset)
|
|
455
|
-
form.json ← form control tokens (input, label, checkbox, switch…)
|
|
456
|
-
gradient.json ← darkening overlay gradient (colors + responsive stops)
|
|
457
|
-
grid.json ← columns, gap, flex fractions
|
|
458
|
-
icon.json ← SVG icon tokens (data URI)
|
|
459
|
-
motion.json ← duration, easing, transitions
|
|
460
|
-
opacity.json ← purposeful opacity aliases (disabled, overlay)
|
|
461
|
-
radius.json ← border-radius scale + purposeful aliases
|
|
462
|
-
ratio.json ← aspect-ratio values (16/9, 4/3…)
|
|
463
|
-
shadow.json ← purposeful shadow aliases
|
|
464
|
-
size.json ← T-shirt scale + breakpoint aliases
|
|
465
|
-
spacing.json ← spacing scale + fluid clamp() aliases
|
|
466
|
-
typography.json ← font-size scale (fixed + fluid), heading sizes
|
|
467
|
-
z-index.json ← stacking order
|
|
468
|
-
themes/
|
|
469
|
-
dark.json ← dark color scheme overrides
|
|
86
|
+
npm install
|
|
87
|
+
npm run build # tokens/**/*.json → dist/css/**
|
|
470
88
|
```
|
|
471
89
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
```
|
|
475
|
-
dist/css/
|
|
476
|
-
index.css ← imports everything
|
|
477
|
-
primitive.css ← imports all primitive files
|
|
478
|
-
semantic.css ← imports all semantic files
|
|
479
|
-
primitive/ ← one file per tokens/primitive/*.json
|
|
480
|
-
semantic/ ← one file per tokens/semantic/*.json
|
|
481
|
-
themes/ ← one file per tokens/themes/*.json
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
---
|
|
90
|
+
`dist/` is generated and committed. Never edit it by hand.
|
|
485
91
|
|
|
486
92
|
## References
|
|
487
93
|
|
|
488
|
-
- [DTCG — format and concepts](docs/DTCG.md)
|
|
489
94
|
- [DTCG specification](https://tr.designtokens.org/format/) — W3C Community Group draft
|
|
490
|
-
- [Style Dictionary v5](https://styledictionary.com/)
|
|
491
|
-
- [
|
|
95
|
+
- [Style Dictionary v5](https://styledictionary.com/)
|
|
96
|
+
- [Utopia](https://utopia.fyi) — the fluid scale method
|
|
97
|
+
- [`@uncinq/component-tokens`](https://github.com/uncinq/component-tokens) — the component layer
|
|
98
|
+
- [`@uncinq/css-base`](https://github.com/uncinq/css-base) — the CSS foundation consuming these tokens
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT © [Un Cinq](https://uncinq.dev/)
|