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