Skip to content

Commit ea27679

Browse files
authored
Merge pull request #562 from aziontech/docs/theme-tokens-readme
docs(theme): rewrite tokens README to match build-tokens pipeline
2 parents 2402374 + 23d93cb commit ea27679

1 file changed

Lines changed: 160 additions & 84 deletions

File tree

packages/theme/src/tokens/README.md

Lines changed: 160 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<h1 align="center">azion-theme · tokens</h1>
22

3-
Design tokens organized as JavaScript modules, ready to be compiled into CSS custom properties. Architecture targets **Tailwind CSS v4** (CSS-first `@theme` / single stylesheet) — tokens live in JS as the source-of-truth and are compiled to CSS variables at build time.
3+
Design tokens organized as JavaScript modules, compiled into CSS custom properties and a Tailwind config. The pipeline emits **two parallel targets** from the same source:
4+
5+
- **`dist/v3/`** — Tailwind v3 preset (`tailwind-preset.js` / `tailwind.config.js`) + a self-contained `globals.css` with `@tailwind` directives, `:root` / `[data-theme=dark]` blocks, responsive `@media` overrides, and the component classes.
6+
- **`dist/v4/`** — Tailwind v4 CSS-first output: `globals.css` with `@import "tailwindcss"` + `@theme { … }` + plain `:root` for shape/typography semantics + `@layer components`.
7+
8+
Tokens live in JS as the source-of-truth and are compiled at package build time, not in the browser.
49

510
## 📋 Table of contents
611

712
- [Architecture overview](#-architecture-overview)
813
- [File structure](#-file-structure)
914
- [Token types](#-token-types)
10-
- [Compile & inject](#-compile--inject)
15+
- [Building & consuming](#-building--consuming)
1116
- [How to add a new token](#-how-to-add-a-new-token)
1217
- [Token references (`tokenRef`)](#-token-references-tokenref)
1318
- [Test pages](#-test-pages)
@@ -17,23 +22,31 @@ Design tokens organized as JavaScript modules, ready to be compiled into CSS cus
1722

1823
## 🏗 Architecture overview
1924

20-
Two layers:
25+
Three layers:
2126

22-
1. **Primitives** — raw, theme-invariant values (color hexes, px sizes, font sizes, etc.).
23-
2. **Theme (semantic)** — light/dark variants that **reference** primitives via `tokenRef(...)`.
27+
1. **Primitives** — raw, theme-invariant values (color hexes, px sizes, font sizes, durations).
28+
2. **Semantic / theme** — light/dark variants and responsive (per-breakpoint) tokens that **reference** primitives via `tokenRef(...)`.
29+
3. **Output bundles**`dist/v3/` and `dist/v4/` artifacts emitted by `scripts/build-tokens.mjs`.
2430

2531
Compile pipeline:
2632

2733
```
28-
tokens/primitives/*.js ─┐
29-
├─► scripts/compile-primitives.js ─► :root { --color-… --size-… }
30-
tokens/theme/*.js ─┘ (single block — values don't change)
31-
└─► scripts/compile-theme.js ─► :root, [data-theme=light] { … }
32-
[data-theme=dark] { … }
34+
tokens/primitives/** ─┐
35+
36+
tokens/semantic/colors.js ─┤
37+
tokens/semantic/*.data.js ─┼──► scripts/build-tokens.mjs ──► dist/v3/{globals.css,tailwind-preset.js,tailwind.config.js}
38+
tokens/theme/** ─┤ dist/v4/globals.css
39+
40+
scripts/{compile-primitives, ─┘
41+
compile-theme,
42+
resolve,
43+
css-vars,
44+
refs}.js (building blocks used by build-tokens.mjs)
3345
```
3446

35-
- Primitives are **absolute** and emit one block (`:root, [data-theme=light]`).
36-
- Theme tokens have **light** and **dark** variants and emit two blocks. Only theme vars flip between themes.
47+
- Primitives emit a single block (values don't change between themes).
48+
- Semantic colors flip between `:root, [data-theme=light]` and `[data-theme=dark], .dark, .azion.azion-dark`.
49+
- Responsive semantic tokens (containers, spacings, texts) emit `@media (min-width: …)` overrides.
3750

3851
---
3952

@@ -44,52 +57,75 @@ src/
4457
├── tokens/
4558
│ ├── primitives/
4659
│ │ ├── colors/
47-
│ │ │ ├── colors.js # base, gray, violet, orange, slate, yellow,
48-
│ │ │ │ # green, blue, neutral, red, surface (+ brand, alpha)
49-
│ │ │ ├── brand.js # primary, accent, absolute (brand colors)
60+
│ │ │ ├── colors.js # base, blue, gray, violet, orange, slate, yellow,
61+
│ │ │ │ # green, red + surface palettes + brand re-export
62+
│ │ │ ├── brand.js # primary, accent, surfaces, absolute
5063
│ │ │ └── alpha.js # alpha variants for each palette
5164
│ │ ├── shape/
65+
│ │ │ ├── aspect-video.js
5266
│ │ │ ├── container.js # container-3xs … container-7xl
5367
│ │ │ ├── height.js # h-2 … h-96
54-
│ │ │ ├── radius.js # none, sm, DEFAULT, md … 3xl, full
68+
│ │ │ ├── radius.js # none, sm, DEFAULT, md, lg, xl, 2xl, 3xl, full
69+
│ │ │ ├── shape.js # shape aliases (`max-w-*`, etc.)
5570
│ │ │ ├── size.js # size-2 … size-96
5671
│ │ │ ├── spacing.js # spacing-1 … spacing-96
5772
│ │ │ └── width.js # w-3xs … w-7xl (alias → container.X)
5873
│ │ ├── typography/
59-
│ │ │ ├── font-family.js # sans, code, display
74+
│ │ │ ├── font-family.js # sans, mono, code
6075
│ │ │ ├── font-size.js # text-xs … text-9xl
61-
│ │ │ └── line-height.js # leading-none, leading-3 … leading-10
76+
│ │ │ ├── font-weight.js # 100 … 900
77+
│ │ │ ├── leading.js # leading-3 … leading-10
78+
│ │ │ ├── line-height.js # line-height-none … line-height-loose
79+
│ │ │ └── tracking.js # tracking-tighter … tracking-widest
6280
│ │ ├── effects/
6381
│ │ │ ├── blur.js # blur-xs … blur-3xl
64-
│ │ │ └── opacity.js # opacity-25/50/75/100
82+
│ │ │ ├── drop-shadow.js
83+
│ │ │ ├── inset-shadow.js
84+
│ │ │ ├── opacity.js # opacity-0 … opacity-100
85+
│ │ │ ├── perspective.js
86+
│ │ │ └── shadow.js # box-shadow scale
87+
│ │ ├── animations/
88+
│ │ │ ├── animate.js # named keyframe animations
89+
│ │ │ └── ease.js # easing curves
6590
│ │ ├── border-widths.js # border-0 … border-4
6691
│ │ ├── breakpoints.js # sm, md, lg, xl, 2xl
67-
│ │ └── ring-offset.js # ring-offset
92+
│ │ └── ring-offset.js
6893
│ ├── theme/
69-
│ │ ├── primary.js # primary, primary-mask/selected/hover/active/contrast
70-
│ │ ├── secondary.js # secondary, secondary-*
71-
│ │ ├── accent.js # accent, accent-*
72-
│ │ ├── surfaces.js # surface-0 … surface-950 (aliases for gray)
94+
│ │ ├── primary.js # primary, primary-mask/hover/contrast
95+
│ │ ├── secondary.js
96+
│ │ ├── accent.js
97+
│ │ ├── surfaces.js # surface-0 … surface-950 (alias for gray)
7398
│ │ ├── background.js # bg-canvas, bg-surface, bg-mask, …
74-
│ │ ├── border.js # border-default, border-muted, border-strong, border-selected
99+
│ │ ├── border.js # border-default/muted/strong/selected
75100
│ │ ├── text.js # text-default, text-muted
76101
│ │ ├── ring.js # ring-color
77102
│ │ └── feedback/
78-
│ │ ├── success.js # success, success-border, success-contrast
103+
│ │ ├── success.js
79104
│ │ ├── warning.js
80105
│ │ ├── danger.js
81106
│ │ └── info.js
82-
│ └── semantic/ # legacy semantic colors (pre-v4 pipeline)
83-
│ └── colors.js
107+
│ ├── semantic/
108+
│ │ ├── colors.js # text/background/border semantic refs (consumed by v3 preset)
109+
│ │ ├── containers.js # static + responsive container tokens
110+
│ │ ├── containers.data.js # `{ key: { sm, md, lg, … } }` data table
111+
│ │ ├── spacings.js
112+
│ │ ├── spacings.data.js
113+
│ │ ├── texts.js
114+
│ │ ├── texts.data.js # font-size + line-height + letter-spacing bundles
115+
│ │ └── animations.js
116+
│ ├── theme.js # Tailwind theme.extend (colors + semantic mappings)
117+
│ └── index.js # public re-exports
84118
├── scripts/
85-
│ ├── refs.js # tokenRef helper
86-
│ ├── resolve.js # legacy resolver (semantic/colors.js)
87-
│ ├── css-vars.js # legacy CSS-vars compiler
88-
│ ├── compile-primitives.js # NEW: flattens primitives → :root
89-
│ └── compile-theme.js # NEW: resolves theme refs → :root + [data-theme=dark]
119+
│ ├── refs.js # tokenRef helper + isTokenRef guard
120+
│ ├── resolve.js # resolves `tokenRef` paths to literal values
121+
│ ├── css-vars.js # builds light/dark CSS var maps from semantic refs
122+
│ ├── compile-primitives.js # flattens primitive trees into CSS vars
123+
│ ├── compile-theme.js # legacy theme compiler (used by the harness pages)
124+
│ └── build-tokens.mjs # main entrypoint: emits dist/v3 and dist/v4 bundles
90125
└── tests/
91126
├── primitives.html # visual harness for all primitives
92-
└── theme.html # visual harness with light/dark toggle
127+
├── theme.html # semantic tokens with light/dark toggle
128+
└── tokens.html # combined view
93129
```
94130

95131
---
@@ -102,7 +138,7 @@ Plain JS objects with literal values. Theme-invariant.
102138

103139
```js
104140
// tokens/primitives/colors/brand.js
105-
export const brand = {
141+
export const brandPrimitives = {
106142
primary: { 50: '#FFF5EF', 100: '#FFE7D8', /**/ 500: '#FE601F', /**/ 950: '#401602' },
107143
accent: { 50: '#F6F6FF', /**/ 500: '#8A84EC', /**/ 950: '#0B0A19' },
108144
absolute: { black: '#0A0A0A', white: '#FCFCFC' },
@@ -114,9 +150,9 @@ export const brand = {
114150
export const spacing = { 1: '4px', 2: '8px', /**/ 96: '384px' };
115151
```
116152

117-
### Theme (semantic)
153+
### Semantic / theme
118154

119-
Objects with `light` / `dark` variants. Values are `tokenRef(...)` calls that point to primitives (or to other semantic tokens like `theme.surfaces.surface-X`).
155+
Objects with `light` / `dark` variants. Values are `tokenRef(...)` calls that point to primitives.
120156

121157
```js
122158
// tokens/theme/primary.js
@@ -138,53 +174,89 @@ export const primary = {
138174
};
139175
```
140176

177+
### Responsive semantic data (`*.data.js`)
178+
179+
Containers, spacings, and texts use a per-breakpoint table that `build-tokens.mjs` flattens into `@media` overrides plus matching component classes (`.gap-…`, `.p-…`, `.text-…`, `.px-container`, `.py-container`, `.max-container-width`).
180+
181+
```js
182+
// tokens/semantic/spacings.data.js
183+
export const spacingsData = {
184+
'gap-sm': { _: '8px', md: '12px', xl: '16px' },
185+
'gap-md': { _: '12px', md: '16px', xl: '24px' },
186+
//
187+
};
188+
```
189+
190+
`_` is the base value emitted in `:root`; the breakpoint keys (`sm`, `md`, `lg`, `xl`, `2xl`) become media-query overrides.
191+
141192
---
142193

143-
## 🔧 Compile & inject
194+
## 🔧 Building & consuming
195+
196+
### Building the package
197+
198+
```bash
199+
# emit both v3 and v4 bundles
200+
pnpm --filter @aziontech/theme build:tokens
201+
202+
# only one target
203+
pnpm --filter @aziontech/theme build:tokens:v3
204+
pnpm --filter @aziontech/theme build:tokens:v4
205+
206+
# v3 + compile final CSS with tailwindcss
207+
pnpm --filter @aziontech/theme build:css:v3
208+
```
209+
210+
Outputs land in `packages/theme/dist/v3/` and `packages/theme/dist/v4/`.
144211

145-
### In the browser
212+
### Consuming in a Tailwind v3 project
146213

147214
```js
148-
import { injectPrimitivesCss } from '@aziontech/theme/scripts/compile-primitives.js';
149-
import { injectThemeCss } from '@aziontech/theme/scripts/compile-theme.js';
215+
// tailwind.config.js
216+
import themePreset from '@aziontech/theme/tailwind-preset/v3'
150217

151-
injectPrimitivesCss(); // <style data-azion-primitives> with all primitive vars
152-
injectThemeCss(); // <style data-azion-theme> with light+dark theme vars
218+
export default {
219+
presets: [themePreset],
220+
content: ['./src/**/*.{vue,js,ts,jsx,tsx}'],
221+
darkMode: ['class'],
222+
}
223+
```
224+
225+
```css
226+
/* main.css */
227+
@import '@aziontech/theme/v3/globals.css';
153228
```
154229

155-
Then use the variables anywhere:
230+
Utilities like `text-default`, `bg-surface`, `border-default`, `text-primary`, `gap-md`, `p-md`, `.text-heading-lg`, etc. are then available everywhere.
231+
232+
### Consuming in a Tailwind v4 project
156233

157234
```css
158-
.btn {
159-
background: var(--primary);
160-
color: var(--primary-contrast);
161-
padding: var(--spacing-2) var(--spacing-4);
162-
border-radius: var(--radius-md);
163-
font-size: var(--font-size-sm);
164-
}
165-
.btn:hover { background: var(--primary-hover); }
235+
/* main.css */
236+
@import '@aziontech/theme/v4/globals.css';
166237
```
167238

168-
### As CSS strings (Node / SSR / build step)
239+
No `tailwind.config.js` needed — v4 reads `@theme { … }` directly from the imported CSS.
240+
241+
### As JS objects (Node / build steps)
169242

170243
```js
171-
import { compilePrimitivesCss } from '@aziontech/theme/scripts/compile-primitives.js';
172-
import { compileThemeCss } from '@aziontech/theme/scripts/compile-theme.js';
244+
import { createCssVars, cssVarsString } from '@aziontech/theme';
173245

174-
const css = compilePrimitivesCss() + '\n' + compileThemeCss();
175-
fs.writeFileSync('dist/theme.css', css);
246+
createCssVars(); // → { light: { '--text-default': '#1A1A1A', … }, dark: { … } }
247+
cssVarsString(); // → ':root, [data-theme=light], .azion.azion-light { … } [data-theme=dark], … { … }'
176248
```
177249

178-
### As JS objects
250+
### Injecting at runtime (browser)
179251

180252
```js
181-
import { compilePrimitivesVars } from '@aziontech/theme/scripts/compile-primitives.js';
182-
import { compileThemeVars } from '@aziontech/theme/scripts/compile-theme.js';
253+
import { injectCssVars } from '@aziontech/theme';
183254

184-
compilePrimitivesVars(); // → { '--color-orange-500': '#FE601F', '--spacing-1': '4px', … }
185-
compileThemeVars(); // → { light: {…}, dark: {…} }
255+
injectCssVars(); // appends a <style data-azion-tokens> element to <head>
186256
```
187257

258+
Only useful if you can't precompile and import `globals.css` — production usage should prefer the static stylesheet.
259+
188260
---
189261

190262
## ➕ How to add a new token
@@ -201,7 +273,7 @@ compileThemeVars(); // → { light: {…}, dark: {…} }
201273
};
202274
```
203275

204-
3. Done. It is automatically picked up by `compile-primitives.js`, producing `--spacing-128: 512px`.
276+
3. Rebuild (`pnpm --filter @aziontech/theme build:tokens`). The compiler picks it up and emits `--spacing-128: 512px`.
205277

206278
### Add a new semantic (theme) token
207279

@@ -222,34 +294,36 @@ compileThemeVars(); // → { light: {…}, dark: {…} }
222294
};
223295
```
224296

225-
3. The compile script picks it up automatically — emits `--border-emphasis` inside both blocks.
297+
3. Rebuild. The new variable lands in both `:root` and `[data-theme=dark]` blocks.
226298

227-
### Add a new semantic group (new file)
299+
### Add a new responsive semantic token
228300

229-
1. Create `tokens/theme/<group>.js` exporting `{ light, dark }`.
230-
2. Register it in `scripts/compile-theme.js`:
301+
1. Add an entry to the relevant `*.data.js` file in `tokens/semantic/`:
231302

232303
```js
233-
import { yourGroup } from '../tokens/theme/your-group.js';
234-
/* … inside compileVariant(variant): */
235-
const groups = [ /* …, */ yourGroup[variant] ];
304+
// tokens/semantic/spacings.data.js
305+
export const spacingsData = {
306+
/**/
307+
'gap-2xl': { _: '32px', md: '40px', xl: '56px' },
308+
};
236309
```
237310

311+
2. Rebuild. `build-tokens.mjs` emits the base var in `:root`, per-breakpoint `@media` overrides, **and** a matching `.gap-2xl { gap: var(--gap-2xl) }` utility.
312+
238313
---
239314

240315
## 🔗 Token references (`tokenRef`)
241316

242-
`tokenRef(path)` returns a marker object `{ __ref: path }` that the compiler resolves at build time.
243-
244-
Supported path prefixes (handled by `scripts/compile-theme.js`):
317+
`tokenRef(path)` returns a marker object `{ __ref: path }` that the compiler resolves at build time. Supported path prefixes (see `scripts/resolve.js`):
245318

246319
| Prefix | Looks up |
247320
|---|---|
248321
| `primitives.X.Y.Z` | `tokens/primitives/colors/colors.js` tree (e.g., `primitives.gray.900`, `primitives.alpha.orange.100`) |
249-
| `brand.primary.primary-N` | `brand.primary[N]` (e.g., `brand.primary.primary-500``#FE601F`) |
250-
| `brand.accent.accent-N` | `brand.accent[N]` |
251-
| `brand.absolute.X` | `brand.absolute.X` (`black` / `white`) |
252-
| `theme.surfaces.surface-N` | the semantic surface map (chain: surface → gray primitive) |
322+
| `surfacePrimitives.surface.N` | the surface palette (used internally; usually referenced via `brand.surfaces.…`) |
323+
| `brand.surfaces.surface-N` | `surfacePrimitives.surface[N]` (e.g., `brand.surfaces.surface-100``#F5F5F5`) |
324+
| `brand.primary.primary-N` | `brandPrimitives.primary[N]` (e.g., `brand.primary.primary-500``#FE601F`) |
325+
| `brand.accent.accent-N` | `brandPrimitives.accent[N]` |
326+
| `brand.absolute.X` | `brandPrimitives.absolute.X` (`black` / `white`) |
253327

254328
Refs of unknown prefixes are left as the raw path string in the output — flag for "this needs resolver support".
255329

@@ -263,8 +337,9 @@ Local visual harnesses (require a static server because of ESM imports):
263337
npx http-server packages/theme/src -p 8080
264338
```
265339

266-
- `http://localhost:8080/tests/primitives.html` — all primitives rendered as swatches/scales (396 vars).
267-
- `http://localhost:8080/tests/theme.html` — semantic tokens with a **light/dark toggle button** (59 + 59 vars). Body, buttons, alerts, surfaces, and swatches all react to the toggle.
340+
- `http://localhost:8080/tests/primitives.html` — every primitive rendered as a swatch / scale.
341+
- `http://localhost:8080/tests/theme.html` — semantic tokens with a **light/dark toggle**.
342+
- `http://localhost:8080/tests/tokens.html` — combined view.
268343

269344
---
270345

@@ -285,16 +360,17 @@ The compiled CSS targets multiple hooks so the runtime can pick whichever conven
285360
Switching is a one-liner:
286361

287362
```js
363+
// data-attribute strategy
288364
document.documentElement.setAttribute('data-theme', 'dark'); // or 'light'
289-
```
290-
291-
Or, if you prefer Tailwind's `dark` class strategy:
292365

293-
```js
366+
// Tailwind class strategy
294367
document.documentElement.classList.toggle('dark');
368+
369+
// Azion namespaced classes (use one or the other)
370+
document.documentElement.classList.add('azion', 'azion-dark');
295371
```
296372

297-
Both work because the selectors cover both conventions.
373+
All three strategies hit the same set of CSS variables.
298374

299375
---
300376

0 commit comments

Comments
 (0)