@fastwork/xosmoz-theme 0.48.0 → 0.50.0

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/dist/llms.txt ADDED
@@ -0,0 +1,822 @@
1
+ # @fastwork/xosmoz-theme — Design Tokens Reference
2
+
3
+ > Design token system for Xosmoz. Uses OKLCH color space. Supports light and dark themes via CSS variables and `data-theme` attribute. This guide covers color tokens, typography tokens, and box shadows (CSS variables).
4
+
5
+ ---
6
+
7
+ ## Setup
8
+
9
+ ### Install
10
+
11
+ ```bash
12
+ npm install @fastwork/xosmoz-theme
13
+ ```
14
+
15
+ ### CSS Imports
16
+
17
+ ```css
18
+ /* Base styles: CSS reset, font imports, typography/spacing tokens */
19
+ @import '@fastwork/xosmoz-theme/base.css';
20
+
21
+ /* All themes (light + dark) */
22
+ @import '@fastwork/xosmoz-theme/themes.css';
23
+
24
+ /* OR import a specific theme only */
25
+ @import '@fastwork/xosmoz-theme/themes/light.css';
26
+ @import '@fastwork/xosmoz-theme/themes/dark.css';
27
+ ```
28
+
29
+ ### Theme Switching
30
+
31
+ Light theme is the default. Set `data-theme="dark"` on `<html>` for dark mode.
32
+
33
+ ```html
34
+ <html>...</html> <!-- Light theme (default) -->
35
+ <html data-theme="dark">...</html> <!-- Dark theme -->
36
+ ```
37
+
38
+ ```javascript
39
+ // Switch at runtime
40
+ document.documentElement.setAttribute('data-theme', 'dark');
41
+ document.documentElement.removeAttribute('data-theme'); // back to light
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Color Token Architecture
47
+
48
+ The system has two layers:
49
+
50
+ ### Layer 1 — Palette Tokens (primitives)
51
+ Eight raw color values. These are single CSS variables (no numbered scales). They are the same in both light and dark themes.
52
+
53
+ ```
54
+ --xz-color-fastwork (brand blue-purple)
55
+ --xz-color-gray
56
+ --xz-color-green
57
+ --xz-color-yellow
58
+ --xz-color-red
59
+ --xz-color-orange
60
+ --xz-color-purple
61
+ --xz-color-cyan
62
+ ```
63
+
64
+ **Rule: Do not use palette tokens directly in UI.** They exist as internal primitives. Always use semantic tokens instead.
65
+
66
+ ### Layer 2 — Semantic Tokens (theme-aware)
67
+ These tokens change values between light and dark themes. Always use these for UI components, text, borders, and backgrounds.
68
+
69
+ Pattern: `--xz-color-{category}-{property}-{scale}`
70
+
71
+ ---
72
+
73
+ ## Base Tokens
74
+
75
+ Base tokens cover surfaces, text, borders, and neutral overlays. They are theme-aware (change in dark mode).
76
+
77
+ | CSS Variable | Purpose | When to use |
78
+ |---|---|---|
79
+ | `--xz-color-surface-50` | Absolute white/darkest background | Iframe embed background, fullscreen canvas, outermost page wrapper |
80
+ | `--xz-color-surface-100` | Default page background | Main app body, full-page background, content area behind cards |
81
+ | `--xz-color-surface-200` | Elevated surface | Cards, modals, dialogs, sidebar panels, sheet bottoms |
82
+ | `--xz-color-surface-300` | Secondary elevated surface | Nested card inside a card, accordion content area, secondary panel, table header row background |
83
+ | `--xz-color-surface-400` | Tertiary elevated surface | Popover, dropdown menu, tooltip, context menu, autocomplete suggestion list, command palette |
84
+ | `--xz-color-bg-100` | Extreme base (near-black in light) | Footer dark band, inverted hero section, dark navbar, cookie banner |
85
+ | `--xz-color-bg-200` | Secondary extreme base | Dark sidebar variant, secondary inverted section, code block background |
86
+ | `--xz-color-fg-100` | Text/icon on bg-100 / bg-200 | White text on dark footer, icon on inverted navbar, button text on dark hero CTA |
87
+ | `--xz-color-text-100` | Primary text | Body text, headings, button labels, nav links, input values, table cell text |
88
+ | `--xz-color-text-200` | Secondary / muted text (70%) | Timestamps, helper text below inputs, subtitle under heading, metadata, breadcrumb inactive items |
89
+ | `--xz-color-text-300` | Disabled / placeholder text (60%) | Input placeholder, disabled button text, watermark text, empty state hint |
90
+ | `--xz-color-soft-100` | Very subtle neutral overlay (5%) | Hover highlight on table rows, subtle active state on sidebar items, zebra stripe alternate row |
91
+ | `--xz-color-soft-200` | Subtle neutral overlay (10%) | Selected/active row in list, pressed state on neutral elements, drag-over drop zone highlight |
92
+ | `--xz-color-line-50` | Subtle border | Horizontal divider between list items, subtle card border, separator between nav sections |
93
+ | `--xz-color-line-100` | Default border | Input border, card border, table cell border, tab bar bottom border |
94
+ | `--xz-color-line-200` | Emphasized border | Active/focus input border, emphasized separator, selected tab underline, strong divider |
95
+ | `--xz-color-line-300` | Strongest border | Maximum emphasis divider, high-contrast separator, strongest active outline |
96
+
97
+ ---
98
+
99
+ ## Semantic Tokens
100
+
101
+ Eight categories. Categories: `primary`, `danger`, `success`, `warning`, `info`, `neutral`, `orange`, `purple`. Most categories have 11 tokens; `neutral` has 12 (includes an extra `text-200`).
102
+
103
+ ### Token Pattern per Category
104
+
105
+ Replace `{name}` with the category:
106
+
107
+ | CSS Variable | Purpose |
108
+ |---|---|
109
+ | `--xz-color-{name}-soft-100` | Tinted transparent background — lightest (10% opacity of base color) |
110
+ | `--xz-color-{name}-soft-200` | Tinted transparent background — stronger (16% opacity), use for hover |
111
+ | `--xz-color-{name}-soft-solid-100` | Opaque version of soft-100 (mixed into surface-100) — use on overlapping surfaces |
112
+ | `--xz-color-{name}-soft-solid-200` | Opaque version of soft-200 |
113
+ | `--xz-color-{name}-line-100` | Subtle colored border |
114
+ | `--xz-color-{name}-line-200` | Default colored border |
115
+ | `--xz-color-{name}-line-300` | Strong colored border |
116
+ | `--xz-color-{name}-bg-100` | Solid fill background — buttons, chips, badges |
117
+ | `--xz-color-{name}-bg-200` | Solid fill hover/pressed state |
118
+ | `--xz-color-{name}-fg-100` | Text/icon on solid bg — **always pair with bg-100 / bg-200** |
119
+ | `--xz-color-{name}-text-100` | Colored text on light surfaces — links, status labels |
120
+
121
+ > **`neutral` exception:** also includes `--xz-color-neutral-text-200` — Muted neutral text, lighter than `text-100`. Use for supporting content, secondary labels.
122
+
123
+ ### Semantic Category Usage Map
124
+
125
+ When choosing a category, match the **intent** of the UI element:
126
+
127
+ | Category | Intent | Example scenarios |
128
+ |---|---|---|
129
+ | `primary` | Brand action, main interaction | CTA buttons, links, active tab indicator, selected checkbox/radio, progress bar fill, focused input ring, pagination current page |
130
+ | `danger` | Destructive action, error state | Delete/remove buttons, form validation error message, declined/rejected status badge, overdue indicator, "leave without saving" prompt |
131
+ | `success` | Positive outcome, completion | Success alert/toast, "completed" status badge, verified checkmark, approval indicator, "payment received" label, online status dot |
132
+ | `warning` | Caution, attention needed | Warning banner, "expiring soon" label, pending review status, low stock indicator, unsaved changes notice, trial ending alert |
133
+ | `info` | Informational, neutral notice | Info banner, help tooltip, "new feature" badge, system maintenance notice, changelog highlight, onboarding tip |
134
+ | `neutral` | Non-semantic, default UI | Secondary/ghost buttons, default tags/chips, inactive tabs, neutral badges (count), toggle off state, breadcrumb separator |
135
+ | `orange` | Special accent | Premium/pro plan badge, trending indicator, notification dot, highlight/featured card border, "hot" label, points/rewards |
136
+ | `purple` | Special accent | AI-powered feature badge, creator/author tag, premium tier indicator, special promotion label, "beta" badge |
137
+
138
+ ### Key Rules for Semantic Tokens
139
+
140
+ - **`bg-100` + `fg-100`**: Solid fill components (buttons, solid badges). `fg-100` provides correct contrast (white or dark depending on the category color).
141
+ - **`soft-100` + `text-100` + `line-100`**: Soft/tinted style (alerts, soft badges, highlighted rows).
142
+ - **`soft-solid-*`**: Use instead of `soft-*` when the component sits on a non-transparent surface. Example: a success badge inside a card (`surface-200`) — use `soft-solid-100` so the tint mixes with the surface color and doesn't show the card color bleeding through. If the badge were on the page background directly, `soft-100` (transparent) is fine.
143
+ - **`text-100`**: Use for colored text that sits on surface backgrounds (not on solid fills). Examples: link text, status label text, tag text.
144
+ - **`line-*`**: Colored borders. Use `line-200` as the default, `line-100` for subtle, `line-300` for strong/emphasized.
145
+
146
+ ---
147
+
148
+ ## Alpha & Overlay Tokens
149
+
150
+ These use a 100–1000 scale representing opacity from 0.1 to 1.0.
151
+
152
+ ```
153
+ --xz-color-black-alpha-100 (black at 10% opacity)
154
+ --xz-color-black-alpha-200 (black at 20% opacity)
155
+ ...
156
+ --xz-color-black-alpha-1000 (black at 100% opacity)
157
+
158
+ --xz-color-white-alpha-100 (white at 10% opacity)
159
+ ...
160
+ --xz-color-white-alpha-1000 (white at 100% opacity)
161
+
162
+ --xz-color-overlay-100 (dark overlay at 10% opacity)
163
+ ...
164
+ --xz-color-overlay-1000 (dark overlay at 100% opacity)
165
+ ```
166
+
167
+ ### When to use each range
168
+
169
+ | Token range | Scenario |
170
+ |---|---|
171
+ | `--xz-color-overlay-400` to `--xz-color-overlay-600` | Modal/dialog backdrop (use `overlay-600` as default), drawer overlay, confirmation prompt scrim |
172
+ | `--xz-color-overlay-800` to `--xz-color-overlay-1000` | Immersive overlay — image lightbox, fullscreen video player, focus mode |
173
+ | `--xz-color-black-alpha-100` to `--xz-color-black-alpha-300` | Skeleton loader shimmer, disabled state overlay on light surfaces, subtle shadow layers |
174
+ | `--xz-color-black-alpha-400` to `--xz-color-black-alpha-600` | Text protection gradient on images (e.g., card hero image with title overlay), thumbnail scrim, image caption background |
175
+ | `--xz-color-white-alpha-100` to `--xz-color-white-alpha-300` | Glass/frost effect on dark backgrounds, subtle glow behind floating elements, dark-mode hover highlight |
176
+ | `--xz-color-white-alpha-400` to `--xz-color-white-alpha-600` | Text protection on dark images, light scrim for readability on dark hero sections |
177
+
178
+ ---
179
+
180
+ ## Usage Guidelines
181
+
182
+ ### Backgrounds & Surfaces
183
+
184
+ | Situation | Use |
185
+ |---|---|
186
+ | Page background | `--xz-color-surface-100` |
187
+ | Card, modal, dialog | `--xz-color-surface-200` |
188
+ | Nested card or secondary panel | `--xz-color-surface-300` |
189
+ | Popover, dropdown, tooltip | `--xz-color-surface-400` |
190
+ | Inverted/dark block on page | `--xz-color-bg-100` |
191
+ | Text on inverted block | `--xz-color-fg-100` |
192
+
193
+ ### Text
194
+
195
+ | Situation | Use |
196
+ |---|---|
197
+ | Body text, headings | `--xz-color-text-100` |
198
+ | Secondary / helper text | `--xz-color-text-200` |
199
+ | Placeholder / disabled text | `--xz-color-text-300` |
200
+ | Colored status text (link, tag, label) | `--xz-color-{name}-text-100` |
201
+
202
+ ### Borders
203
+
204
+ | Situation | Use |
205
+ |---|---|
206
+ | Subtle separator, divider | `--xz-color-line-50` |
207
+ | Default input or card border | `--xz-color-line-100` |
208
+ | Strong border, active outline | `--xz-color-line-200` |
209
+ | Strongest border, maximum emphasis | `--xz-color-line-300` |
210
+ | Colored border (semantic) | `--xz-color-{name}-line-100/200/300` |
211
+
212
+ ### Solid Components (Buttons, Solid Badges)
213
+
214
+ ```
215
+ background: --xz-color-{name}-bg-100
216
+ color: --xz-color-{name}-fg-100 ← always use fg-100 on bg-100
217
+ hover bg: --xz-color-{name}-bg-200
218
+ ```
219
+
220
+ ### Soft/Tinted Components (Alerts, Soft Badges)
221
+
222
+ ```
223
+ background: --xz-color-{name}-soft-100
224
+ color: --xz-color-{name}-text-100
225
+ border: 1px solid --xz-color-{name}-line-100
226
+ ```
227
+
228
+ ### Outline/Ghost Components
229
+
230
+ ```
231
+ background: transparent
232
+ color: --xz-color-{name}-text-100
233
+ border: 1px solid --xz-color-{name}-line-200
234
+ ```
235
+
236
+ ### Focus Ring
237
+
238
+ ```
239
+ border-color: --xz-color-{name}-line-200
240
+ box-shadow: 0 0 0 3px --xz-color-{name}-soft-100
241
+ ```
242
+
243
+ ### Modal Backdrop
244
+
245
+ ```
246
+ background: --xz-color-overlay-600
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Common UI Patterns
252
+
253
+ ### Primary Button
254
+
255
+ ```css
256
+ .btn-primary {
257
+ background: var(--xz-color-primary-bg-100);
258
+ color: var(--xz-color-primary-fg-100);
259
+ border: none;
260
+ }
261
+ .btn-primary:hover {
262
+ background: var(--xz-color-primary-bg-200);
263
+ }
264
+ ```
265
+
266
+ ### Outline Button
267
+
268
+ ```css
269
+ .btn-outline {
270
+ background: transparent;
271
+ color: var(--xz-color-primary-text-100);
272
+ border: 1px solid var(--xz-color-primary-line-200);
273
+ }
274
+ .btn-outline:hover {
275
+ background: var(--xz-color-primary-soft-100);
276
+ }
277
+ ```
278
+
279
+ ### Danger Button
280
+
281
+ ```css
282
+ .btn-danger {
283
+ background: var(--xz-color-danger-bg-100);
284
+ color: var(--xz-color-danger-fg-100);
285
+ border: none;
286
+ }
287
+ .btn-danger:hover {
288
+ background: var(--xz-color-danger-bg-200);
289
+ }
290
+ ```
291
+
292
+ ### Card
293
+
294
+ ```css
295
+ .card {
296
+ background: var(--xz-color-surface-200);
297
+ color: var(--xz-color-text-100);
298
+ border: 1px solid var(--xz-color-line-50);
299
+ }
300
+ ```
301
+
302
+ ### Alert (soft style)
303
+
304
+ ```css
305
+ /* Replace {name} with: success | danger | warning | info */
306
+ .alert-success {
307
+ background: var(--xz-color-success-soft-100);
308
+ color: var(--xz-color-success-text-100);
309
+ border: 1px solid var(--xz-color-success-line-100);
310
+ }
311
+ .alert-danger {
312
+ background: var(--xz-color-danger-soft-100);
313
+ color: var(--xz-color-danger-text-100);
314
+ border: 1px solid var(--xz-color-danger-line-100);
315
+ }
316
+ .alert-warning {
317
+ background: var(--xz-color-warning-soft-100);
318
+ color: var(--xz-color-warning-text-100);
319
+ border: 1px solid var(--xz-color-warning-line-100);
320
+ }
321
+ .alert-info {
322
+ background: var(--xz-color-info-soft-100);
323
+ color: var(--xz-color-info-text-100);
324
+ border: 1px solid var(--xz-color-info-line-100);
325
+ }
326
+ ```
327
+
328
+ ### Badge — Soft
329
+
330
+ ```css
331
+ .badge-success {
332
+ background: var(--xz-color-success-soft-100);
333
+ color: var(--xz-color-success-text-100);
334
+ }
335
+ ```
336
+
337
+ ### Badge — Solid
338
+
339
+ ```css
340
+ .badge-danger-solid {
341
+ background: var(--xz-color-danger-bg-100);
342
+ color: var(--xz-color-danger-fg-100);
343
+ }
344
+ ```
345
+
346
+ ### Form Input
347
+
348
+ ```css
349
+ .input {
350
+ background: var(--xz-color-surface-100);
351
+ color: var(--xz-color-text-100);
352
+ border: 1px solid var(--xz-color-line-100);
353
+ }
354
+ .input::placeholder {
355
+ color: var(--xz-color-text-300);
356
+ }
357
+ .input:focus {
358
+ border-color: var(--xz-color-primary-line-200);
359
+ box-shadow: 0 0 0 3px var(--xz-color-primary-soft-100);
360
+ outline: none;
361
+ }
362
+ .input.error {
363
+ border-color: var(--xz-color-danger-line-200);
364
+ box-shadow: 0 0 0 3px var(--xz-color-danger-soft-100);
365
+ }
366
+ ```
367
+
368
+ ### Text Hierarchy
369
+
370
+ ```css
371
+ .text-primary { color: var(--xz-color-text-100); }
372
+ .text-muted { color: var(--xz-color-text-200); }
373
+ .text-disabled { color: var(--xz-color-text-300); }
374
+ .text-link { color: var(--xz-color-primary-text-100); }
375
+ .text-danger { color: var(--xz-color-danger-text-100); }
376
+ .text-success { color: var(--xz-color-success-text-100); }
377
+ ```
378
+
379
+ ### Navbar
380
+
381
+ ```css
382
+ .navbar {
383
+ background: var(--xz-color-surface-200);
384
+ border-bottom: 1px solid var(--xz-color-line-50);
385
+ box-shadow: var(--xz-shadow-100);
386
+ }
387
+ .navbar-link { color: var(--xz-color-text-200); }
388
+ .navbar-link:hover { color: var(--xz-color-text-100); }
389
+ .navbar-link.active { color: var(--xz-color-primary-text-100); }
390
+ ```
391
+
392
+ ### Sidebar Navigation
393
+
394
+ ```css
395
+ .sidebar { background: var(--xz-color-surface-200); }
396
+ .sidebar-item { color: var(--xz-color-text-200); }
397
+ .sidebar-item:hover {
398
+ background: var(--xz-color-soft-100);
399
+ color: var(--xz-color-text-100);
400
+ }
401
+ .sidebar-item.active {
402
+ background: var(--xz-color-primary-soft-100);
403
+ color: var(--xz-color-primary-text-100);
404
+ }
405
+ .sidebar-section-label {
406
+ font: var(--xz-font-subtitle3-bold);
407
+ color: var(--xz-color-text-300);
408
+ }
409
+ ```
410
+
411
+ ### Data Table
412
+
413
+ ```css
414
+ .table { border: 1px solid var(--xz-color-line-100); }
415
+ .table th { background: var(--xz-color-surface-300); font: var(--xz-font-subtitle2-bold); color: var(--xz-color-text-100); }
416
+ .table td { border-top: 1px solid var(--xz-color-line-50); font: var(--xz-font-body2); color: var(--xz-color-text-100); }
417
+ .table tr:hover { background: var(--xz-color-soft-100); }
418
+ .table tr.selected { background: var(--xz-color-primary-soft-100); }
419
+ ```
420
+
421
+ ### Tabs
422
+
423
+ ```css
424
+ .tab { color: var(--xz-color-neutral-text-100); border-bottom: 2px solid transparent; }
425
+ .tab:hover { color: var(--xz-color-text-100); background: var(--xz-color-soft-100); }
426
+ .tab.active { color: var(--xz-color-primary-text-100); border-bottom-color: var(--xz-color-primary-bg-100); }
427
+ ```
428
+
429
+ ### Toast / Snackbar
430
+
431
+ ```css
432
+ /* Replace {name} with: success | danger | warning | info */
433
+ .toast {
434
+ background: var(--xz-color-success-soft-solid-100);
435
+ color: var(--xz-color-success-text-100);
436
+ border-left: 4px solid var(--xz-color-success-bg-100);
437
+ box-shadow: var(--xz-shadow-300);
438
+ }
439
+ ```
440
+
441
+ ### Chip / Tag with Remove
442
+
443
+ ```css
444
+ .chip {
445
+ background: var(--xz-color-neutral-soft-100);
446
+ color: var(--xz-color-neutral-text-100);
447
+ border: 1px solid var(--xz-color-neutral-line-100);
448
+ font: var(--xz-font-body3);
449
+ }
450
+ .chip-remove { color: var(--xz-color-neutral-text-200); }
451
+ .chip-remove:hover { color: var(--xz-color-danger-text-100); }
452
+ ```
453
+
454
+ ### Avatar with Status Dot
455
+
456
+ ```css
457
+ .status-dot { border: 2px solid var(--xz-color-surface-200); }
458
+ .status-dot.online { background: var(--xz-color-success-bg-100); }
459
+ .status-dot.busy { background: var(--xz-color-danger-bg-100); }
460
+ .status-dot.away { background: var(--xz-color-warning-bg-100); }
461
+ .status-dot.offline { background: var(--xz-color-neutral-bg-100); }
462
+ ```
463
+
464
+ ### Skeleton Loader
465
+
466
+ ```css
467
+ .skeleton {
468
+ background: var(--xz-color-soft-200);
469
+ border-radius: 4px;
470
+ animation: skeleton-pulse 1.5s ease-in-out infinite;
471
+ }
472
+ @keyframes skeleton-pulse {
473
+ 50% { opacity: 0.5; }
474
+ }
475
+ ```
476
+
477
+ ### Pricing Card
478
+
479
+ ```css
480
+ .pricing-card { background: var(--xz-color-surface-200); border: 1px solid var(--xz-color-line-50); }
481
+ .pricing-card.featured { border-color: var(--xz-color-primary-line-200); }
482
+ .pricing-price { font: var(--xz-font-heading5); color: var(--xz-color-text-100); }
483
+ .pricing-period { font: var(--xz-font-body3); color: var(--xz-color-text-200); }
484
+ .pricing-cta-primary { background: var(--xz-color-primary-bg-100); color: var(--xz-color-primary-fg-100); }
485
+ .pricing-cta-secondary { background: transparent; color: var(--xz-color-neutral-text-100); border: 1px solid var(--xz-color-neutral-line-200); }
486
+ ```
487
+
488
+ ### Stepper / Progress
489
+
490
+ ```css
491
+ .step.completed .step-circle { background: var(--xz-color-success-bg-100); color: var(--xz-color-success-fg-100); }
492
+ .step.current .step-circle { background: var(--xz-color-primary-bg-100); color: var(--xz-color-primary-fg-100); }
493
+ .step.pending .step-circle { background: var(--xz-color-neutral-soft-100); color: var(--xz-color-neutral-text-200); }
494
+ .step-connector.completed { background: var(--xz-color-success-bg-100); }
495
+ .step-connector.pending { background: var(--xz-color-line-100); }
496
+ ```
497
+
498
+ ---
499
+
500
+ ## Figma Variables Guide
501
+
502
+ Figma variables mirror the CSS token system exactly. Every CSS variable has a corresponding Figma variable in the same collection.
503
+
504
+ ### Collection Structure
505
+
506
+ - **Collection name**: `colors`
507
+ - **Modes**: `Light` and `Dark`
508
+ - **Total variables**: ~160 color variables
509
+
510
+ ### Naming Pattern
511
+
512
+ Figma uses slash-separated paths. The mapping to CSS is direct:
513
+
514
+ | Variable type | Figma path | CSS variable |
515
+ |---|---|---|
516
+ | Base theme token | `theme/base/{property}/{scale}` | `--xz-color-{property}-{scale}` |
517
+ | Semantic theme token | `theme/{category}/{property}/{scale}` | `--xz-color-{category}-{property}-{scale}` |
518
+ | Alpha token | `theme/blackAlpha/{scale}` | `--xz-color-black-alpha-{scale}` |
519
+ | Alpha token | `theme/whiteAlpha/{scale}` | `--xz-color-white-alpha-{scale}` |
520
+ | Overlay token | `theme/overlay/{scale}` | `--xz-color-overlay-{scale}` |
521
+ | Palette primitive | `palette/{name}` | `--xz-color-{name}` |
522
+
523
+ ### Complete Figma → CSS Mapping
524
+
525
+ #### Base (17 variables)
526
+
527
+ | Figma Variable | CSS Variable |
528
+ |---|---|
529
+ | `theme/base/bg/100` | `--xz-color-bg-100` |
530
+ | `theme/base/bg/200` | `--xz-color-bg-200` |
531
+ | `theme/base/surface/50` | `--xz-color-surface-50` |
532
+ | `theme/base/surface/100` | `--xz-color-surface-100` |
533
+ | `theme/base/surface/200` | `--xz-color-surface-200` |
534
+ | `theme/base/surface/300` | `--xz-color-surface-300` |
535
+ | `theme/base/surface/400` | `--xz-color-surface-400` |
536
+ | `theme/base/fg/100` | `--xz-color-fg-100` |
537
+ | `theme/base/soft/100` | `--xz-color-soft-100` |
538
+ | `theme/base/soft/200` | `--xz-color-soft-200` |
539
+ | `theme/base/text/100` | `--xz-color-text-100` |
540
+ | `theme/base/text/200` | `--xz-color-text-200` |
541
+ | `theme/base/text/300` | `--xz-color-text-300` |
542
+ | `theme/base/line/50` | `--xz-color-line-50` |
543
+ | `theme/base/line/100` | `--xz-color-line-100` |
544
+ | `theme/base/line/200` | `--xz-color-line-200` |
545
+ | `theme/base/line/300` | `--xz-color-line-300` |
546
+
547
+ #### Semantic Categories (11 variables each; neutral has 12)
548
+
549
+ Same pattern for all 8 categories: `primary`, `danger`, `success`, `warning`, `info`, `neutral`, `orange`, `purple`.
550
+
551
+ | Figma Variable | CSS Variable |
552
+ |---|---|
553
+ | `theme/{name}/soft/100` | `--xz-color-{name}-soft-100` |
554
+ | `theme/{name}/soft/200` | `--xz-color-{name}-soft-200` |
555
+ | `theme/{name}/softSolid/100` | `--xz-color-{name}-soft-solid-100` |
556
+ | `theme/{name}/softSolid/200` | `--xz-color-{name}-soft-solid-200` |
557
+ | `theme/{name}/line/100` | `--xz-color-{name}-line-100` |
558
+ | `theme/{name}/line/200` | `--xz-color-{name}-line-200` |
559
+ | `theme/{name}/line/300` | `--xz-color-{name}-line-300` |
560
+ | `theme/{name}/bg/100` | `--xz-color-{name}-bg-100` |
561
+ | `theme/{name}/bg/200` | `--xz-color-{name}-bg-200` |
562
+ | `theme/{name}/fg/100` | `--xz-color-{name}-fg-100` |
563
+ | `theme/{name}/text/100` | `--xz-color-{name}-text-100` |
564
+
565
+ > **`neutral` exception:** also includes `theme/neutral/text/200` → `--xz-color-neutral-text-200`.
566
+
567
+ #### Black Alpha (10 variables)
568
+
569
+ | Figma Variable | CSS Variable |
570
+ |---|---|
571
+ | `theme/blackAlpha/100` | `--xz-color-black-alpha-100` |
572
+ | `theme/blackAlpha/200` | `--xz-color-black-alpha-200` |
573
+ | `theme/blackAlpha/300` | `--xz-color-black-alpha-300` |
574
+ | `theme/blackAlpha/400` | `--xz-color-black-alpha-400` |
575
+ | `theme/blackAlpha/500` | `--xz-color-black-alpha-500` |
576
+ | `theme/blackAlpha/600` | `--xz-color-black-alpha-600` |
577
+ | `theme/blackAlpha/700` | `--xz-color-black-alpha-700` |
578
+ | `theme/blackAlpha/800` | `--xz-color-black-alpha-800` |
579
+ | `theme/blackAlpha/900` | `--xz-color-black-alpha-900` |
580
+ | `theme/blackAlpha/1000` | `--xz-color-black-alpha-1000` |
581
+
582
+ #### White Alpha (10 variables)
583
+
584
+ | Figma Variable | CSS Variable |
585
+ |---|---|
586
+ | `theme/whiteAlpha/100` | `--xz-color-white-alpha-100` |
587
+ | `theme/whiteAlpha/200` | `--xz-color-white-alpha-200` |
588
+ | `theme/whiteAlpha/300` | `--xz-color-white-alpha-300` |
589
+ | `theme/whiteAlpha/400` | `--xz-color-white-alpha-400` |
590
+ | `theme/whiteAlpha/500` | `--xz-color-white-alpha-500` |
591
+ | `theme/whiteAlpha/600` | `--xz-color-white-alpha-600` |
592
+ | `theme/whiteAlpha/700` | `--xz-color-white-alpha-700` |
593
+ | `theme/whiteAlpha/800` | `--xz-color-white-alpha-800` |
594
+ | `theme/whiteAlpha/900` | `--xz-color-white-alpha-900` |
595
+ | `theme/whiteAlpha/1000` | `--xz-color-white-alpha-1000` |
596
+
597
+ #### Overlay (10 variables)
598
+
599
+ | Figma Variable | CSS Variable |
600
+ |---|---|
601
+ | `theme/overlay/100` | `--xz-color-overlay-100` |
602
+ | `theme/overlay/200` | `--xz-color-overlay-200` |
603
+ | `theme/overlay/300` | `--xz-color-overlay-300` |
604
+ | `theme/overlay/400` | `--xz-color-overlay-400` |
605
+ | `theme/overlay/500` | `--xz-color-overlay-500` |
606
+ | `theme/overlay/600` | `--xz-color-overlay-600` |
607
+ | `theme/overlay/700` | `--xz-color-overlay-700` |
608
+ | `theme/overlay/800` | `--xz-color-overlay-800` |
609
+ | `theme/overlay/900` | `--xz-color-overlay-900` |
610
+ | `theme/overlay/1000` | `--xz-color-overlay-1000` |
611
+
612
+ #### Palette (8 variables)
613
+
614
+ | Figma Variable | CSS Variable |
615
+ |---|---|
616
+ | `palette/fastwork` | `--xz-color-fastwork` |
617
+ | `palette/gray` | `--xz-color-gray` |
618
+ | `palette/green` | `--xz-color-green` |
619
+ | `palette/yellow` | `--xz-color-yellow` |
620
+ | `palette/red` | `--xz-color-red` |
621
+ | `palette/orange` | `--xz-color-orange` |
622
+ | `palette/purple` | `--xz-color-purple` |
623
+ | `palette/cyan` | `--xz-color-cyan` |
624
+
625
+ ### How to Use Figma Variables in Designs
626
+
627
+ 1. Select a layer in Figma (frame, text, shape, etc.)
628
+ 2. In the Fill or Stroke panel, click the color swatch
629
+ 3. Switch to the **Variables** tab in the color picker
630
+ 4. Pick from the `colors` collection
631
+
632
+ **Rules for Figma:**
633
+ - Always use `theme/*` variables for component fills, text colors, and stroke colors
634
+ - Use `palette/*` variables only as references — not directly on components
635
+ - Use `theme/base/surface/100` for page backgrounds, `theme/base/surface/200` for cards
636
+ - Use `theme/{name}/bg/100` for solid-filled elements and `theme/{name}/fg/100` for text on top of them
637
+ - Switch between Light/Dark previews using the **Mode** toggle on the variable collection
638
+
639
+ ### Keeping Figma in Sync with Code
640
+
641
+ Variables are synced from the npm package via the **XOSMOZ Theme Sync** plugin:
642
+
643
+ 1. Open **Plugins → Development → XOSMOZ Theme Sync** in Figma
644
+ 2. Select your variable collection from the dropdown
645
+ 3. Click **"Check for Updates"** to see if any colors changed
646
+ 4. Click **"Update Variables"** to apply changes to both Light and Dark modes at once
647
+
648
+ If starting a new Figma file, use **"Create New Collection"** to generate all variables from scratch.
649
+
650
+ ---
651
+
652
+ ## Typography Tokens
653
+
654
+ Typography tokens are defined in `base.css` (not theme files). They do not change between light and dark themes.
655
+
656
+ ### Font Families
657
+
658
+ | CSS Variable | Value | Use for |
659
+ |---|---|---|
660
+ | `--xz-font-family-primary` | System sans-serif stack (`-apple-system, system-ui, ...`) | Body text, titles, subtitles, UI labels |
661
+ | `--xz-font-family-secondary` | `"Fastwork", "Noto Sans Thai", "Noto Sans", ...` (system fallbacks) | Display headings (h1–h6) |
662
+
663
+ **Rule:** Use `--xz-font-family-primary` for all UI text. Only use `--xz-font-family-secondary` for large display headings (heading1–heading6).
664
+
665
+ ### Font Size Scale
666
+
667
+ 17 sizes on a numbered scale. Use `--xz-font-size-{key}`.
668
+
669
+ | CSS Variable | rem | px |
670
+ |---|---|---|
671
+ | `--xz-font-size-50` | 0.5rem | 8px |
672
+ | `--xz-font-size-100` | 0.625rem | 10px |
673
+ | `--xz-font-size-200` | 0.75rem | 12px |
674
+ | `--xz-font-size-300` | 0.875rem | 14px |
675
+ | `--xz-font-size-400` | 1rem | 16px (base) |
676
+ | `--xz-font-size-500` | 1.125rem | 18px |
677
+ | `--xz-font-size-600` | 1.25rem | 20px |
678
+ | `--xz-font-size-700` | 1.5rem | 24px |
679
+ | `--xz-font-size-800` | 1.625rem | 26px |
680
+ | `--xz-font-size-900` | 2rem | 32px |
681
+ | `--xz-font-size-1000` | 2.375rem | 38px |
682
+ | `--xz-font-size-1100` | 2.5rem | 40px |
683
+ | `--xz-font-size-1200` | 2.6875rem | 43px |
684
+ | `--xz-font-size-1300` | 3rem | 48px |
685
+ | `--xz-font-size-1400` | 3.5rem | 56px |
686
+ | `--xz-font-size-1500` | 4rem | 64px |
687
+ | `--xz-font-size-1600` | 4.5rem | 72px |
688
+
689
+ ### Line Heights
690
+
691
+ | CSS Variable | Value |
692
+ |---|---|
693
+ | `--xz-line-height-100pct` | 1 |
694
+ | `--xz-line-height-125pct` | 1.25 |
695
+ | `--xz-line-height-135pct` | 1.35 |
696
+ | `--xz-line-height-150pct` | 1.5 (default for all tokens) |
697
+ | `--xz-line-height-165pct` | 1.65 |
698
+ | `--xz-line-height-200pct` | 2 |
699
+
700
+ ### Font Shorthand Tokens
701
+
702
+ These are **CSS font shorthand** variables. Apply with `font: var(--xz-font-{name})`.
703
+
704
+ Each token encodes: `{weight} {size}/{line-height} {font-family}`.
705
+
706
+ #### Headings (secondary font, weight 700, responsive)
707
+
708
+ Headings use `--xz-font-family-secondary` (Fastwork). They scale down on mobile (≤768px).
709
+
710
+ | CSS Variable | Desktop size | Mobile size (≤768px) |
711
+ |---|---|---|
712
+ | `--xz-font-heading1` | 4.5rem (72px) | 3rem (48px) |
713
+ | `--xz-font-heading2` | 4rem (64px) | 2.6875rem (43px) |
714
+ | `--xz-font-heading3` | 3.5rem (56px) | 2.375rem (38px) |
715
+ | `--xz-font-heading4` | 3rem (48px) | 2rem (32px) |
716
+ | `--xz-font-heading5` | 2.5rem (40px) | 1.625rem (26px) |
717
+ | `--xz-font-heading6` | 2rem (32px) | 1.5rem (24px) |
718
+
719
+ #### Titles (primary font, weight 700, responsive)
720
+
721
+ Titles use `--xz-font-family-primary`. They scale down one step on mobile.
722
+
723
+ | CSS Variable | Desktop size | Mobile size (≤768px) |
724
+ |---|---|---|
725
+ | `--xz-font-title1` | 1.5rem (24px) | 1.25rem (20px) |
726
+ | `--xz-font-title2` | 1.25rem (20px) | 1.125rem (18px) |
727
+ | `--xz-font-title3` | 1.125rem (18px) | 1rem (16px) |
728
+ | `--xz-font-title4` | 1rem (16px) | 0.875rem (14px) |
729
+
730
+ #### Subtitles (primary font, responsive)
731
+
732
+ Bold (700) and Regular (400) variants. Scale down one step on mobile.
733
+
734
+ | CSS Variable | Weight | Desktop size | Mobile size (≤768px) |
735
+ |---|---|---|---|
736
+ | `--xz-font-subtitle1-bold` | 700 | 1rem (16px) | 0.875rem (14px) |
737
+ | `--xz-font-subtitle1-regular` | 400 | 1rem (16px) | 0.875rem (14px) |
738
+ | `--xz-font-subtitle2-bold` | 700 | 0.875rem (14px) | 0.75rem (12px) |
739
+ | `--xz-font-subtitle2-regular` | 400 | 0.875rem (14px) | 0.75rem (12px) |
740
+ | `--xz-font-subtitle3-bold` | 700 | 0.75rem (12px) | 0.625rem (10px) |
741
+ | `--xz-font-subtitle3-regular` | 400 | 0.75rem (12px) | 0.625rem (10px) |
742
+
743
+ #### Body (primary font, weight 400, responsive)
744
+
745
+ Body text scales down one step on mobile.
746
+
747
+ | CSS Variable | Desktop size | Mobile size (≤768px) |
748
+ |---|---|---|
749
+ | `--xz-font-body1` | 1rem (16px) | 0.875rem (14px) |
750
+ | `--xz-font-body2` | 0.875rem (14px) | 0.75rem (12px) |
751
+ | `--xz-font-body3` | 0.75rem (12px) | 0.625rem (10px) |
752
+ | `--xz-font-body4` | 0.625rem (10px) | 0.5rem (8px) |
753
+
754
+ ### Typography Usage Guidelines
755
+
756
+ | Situation | Use |
757
+ |---|---|
758
+ | Hero / landing page headline | `--xz-font-heading1` to `--xz-font-heading3` |
759
+ | Section heading | `--xz-font-heading4` to `--xz-font-heading6` |
760
+ | Card title, dialog title, modal header | `--xz-font-title1` to `--xz-font-title3` |
761
+ | Small section title, form group label | `--xz-font-title4` |
762
+ | Emphasized label, table header, stat value | `--xz-font-subtitle1-bold` or `--xz-font-subtitle2-bold` |
763
+ | Supporting label, metadata, timestamp | `--xz-font-subtitle2-regular` or `--xz-font-subtitle3-regular` |
764
+ | Navigation menu item, tab label | `--xz-font-subtitle2-bold` |
765
+ | Breadcrumb text | `--xz-font-body3` or `--xz-font-subtitle3-regular` |
766
+ | Default body text, paragraphs | `--xz-font-body1` or `--xz-font-body2` |
767
+ | Tooltip text | `--xz-font-body3` |
768
+ | Small print, captions, helper text | `--xz-font-body3` |
769
+ | Empty state message | `--xz-font-body1` (message) + `--xz-font-title3` (title) |
770
+ | Price display, number callout | `--xz-font-heading5` or `--xz-font-title1` |
771
+ | Micro text (badges, counters, dots) | `--xz-font-body4` |
772
+
773
+ ### Typography CSS Examples
774
+
775
+ ```css
776
+ /* Apply a font shorthand token */
777
+ h1 { font: var(--xz-font-heading1); }
778
+ h2 { font: var(--xz-font-heading2); }
779
+ p { font: var(--xz-font-body2); }
780
+
781
+ /* Card with title */
782
+ .card-title { font: var(--xz-font-title2); color: var(--xz-color-text-100); }
783
+ .card-body { font: var(--xz-font-body2); color: var(--xz-color-text-200); }
784
+
785
+ /* Form label + helper */
786
+ .label { font: var(--xz-font-subtitle2-bold); color: var(--xz-color-text-100); }
787
+ .helper { font: var(--xz-font-body3); color: var(--xz-color-text-300); }
788
+
789
+ /* Use individual tokens when you need granular control */
790
+ .custom {
791
+ font-family: var(--xz-font-family-primary);
792
+ font-size: var(--xz-font-size-300);
793
+ line-height: var(--xz-line-height-150pct);
794
+ }
795
+ ```
796
+
797
+ ### Responsive Behavior
798
+
799
+ All `--xz-font-*` shorthand tokens are automatically responsive. At `max-width: 768px`, they switch to their mobile sizes via a CSS media query in `base.css`. No extra breakpoint code is needed — just use the token and it adapts.
800
+
801
+ ---
802
+
803
+ ## Box Shadow Tokens
804
+
805
+ Four elevation levels. Theme-aware (shadows are stronger in dark mode).
806
+
807
+ | CSS Variable | Light theme value | Use for |
808
+ |---|---|---|
809
+ | `--xz-shadow-100` | `0 1px 2px 0 rgba(0,0,0,0.08)` | Subtle lift — inline action buttons, tag hover, navbar bottom edge, input focus ring complement |
810
+ | `--xz-shadow-200` | `0 2px 6px 0 rgba(0,0,0,0.10)` | Cards at rest, sticky header on scroll, floating action button, small floating elements |
811
+ | `--xz-shadow-300` | `0 4px 16px 0 rgba(0,0,0,0.12)` | Dropdown menu, popover, autocomplete suggestions, select options list, date picker |
812
+ | `--xz-shadow-400` | `0 8px 20px 0 rgba(0,0,0,0.14)` | Modal dialog, drawer panel, lightbox, command palette, fullscreen overlay panels |
813
+
814
+ ```css
815
+ .btn:hover { box-shadow: var(--xz-shadow-100); }
816
+ .card { box-shadow: var(--xz-shadow-200); }
817
+ .sticky-nav { box-shadow: var(--xz-shadow-200); }
818
+ .dropdown { box-shadow: var(--xz-shadow-300); }
819
+ .popover { box-shadow: var(--xz-shadow-300); }
820
+ .modal { box-shadow: var(--xz-shadow-400); }
821
+ .drawer { box-shadow: var(--xz-shadow-400); }
822
+ ```