@almadar/ui 4.57.4 → 5.0.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.
Files changed (41) hide show
  1. package/dist/avl/index.cjs +270 -69
  2. package/dist/avl/index.js +270 -69
  3. package/dist/components/index.cjs +101 -68
  4. package/dist/components/index.js +101 -68
  5. package/dist/context/index.cjs +199 -0
  6. package/dist/context/index.js +199 -0
  7. package/dist/context/themeTokens.d.ts +1 -1
  8. package/dist/docs/index.cjs +21 -21
  9. package/dist/docs/index.js +21 -21
  10. package/dist/marketing/index.cjs +18 -18
  11. package/dist/marketing/index.js +18 -18
  12. package/dist/providers/index.cjs +101 -68
  13. package/dist/providers/index.js +101 -68
  14. package/dist/runtime/index.cjs +270 -69
  15. package/dist/runtime/index.js +270 -69
  16. package/package.json +2 -2
  17. package/tailwind-preset.cjs +118 -3
  18. package/themes/_contract.md +198 -0
  19. package/themes/almadar-website.css +212 -0
  20. package/themes/almadar.css +210 -0
  21. package/themes/arctic.css +210 -0
  22. package/themes/atelier.css +427 -0
  23. package/themes/copper.css +210 -0
  24. package/themes/ember.css +210 -0
  25. package/themes/forest.css +210 -0
  26. package/themes/gazette.css +411 -0
  27. package/themes/index.css +7 -0
  28. package/themes/kiosk.css +412 -0
  29. package/themes/lavender.css +210 -0
  30. package/themes/midnight.css +210 -0
  31. package/themes/minimalist.css +210 -0
  32. package/themes/neon.css +210 -0
  33. package/themes/ocean.css +210 -0
  34. package/themes/prism.css +406 -0
  35. package/themes/rose.css +210 -0
  36. package/themes/sand.css +210 -0
  37. package/themes/slate.css +210 -0
  38. package/themes/sunset.css +210 -0
  39. package/themes/terminal.css +422 -0
  40. package/themes/trait-wars.css +210 -0
  41. package/themes/wireframe.css +216 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.57.4",
3
+ "version": "5.0.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -129,7 +129,7 @@
129
129
  "typecheck": "tsc --noEmit"
130
130
  },
131
131
  "dependencies": {
132
- "@almadar/core": "^7.16.0",
132
+ "@almadar/core": "^9.0.0",
133
133
  "@almadar/evaluator": ">=2.9.2",
134
134
  "@almadar/logger": "^1.3.0",
135
135
  "@almadar/patterns": "^2.26.0",
@@ -256,8 +256,13 @@ module.exports = {
256
256
  ],
257
257
  theme: {
258
258
  fontFamily: {
259
+ // `sans` continues to read --font-family for backward compat.
259
260
  sans: ['var(--font-family)', 'ui-sans-serif', 'system-ui', 'sans-serif'],
260
261
  mono: ['var(--font-family-mono, ui-monospace)', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'monospace'],
262
+ // Layer 1 Type axis: display + body family slots fall back to --font-family
263
+ // so existing themes that don't define them keep their current look.
264
+ display: ['var(--font-family-display, var(--font-family))', 'ui-sans-serif', 'system-ui', 'sans-serif'],
265
+ body: ['var(--font-family-body, var(--font-family))', 'ui-sans-serif', 'system-ui', 'sans-serif'],
261
266
  },
262
267
  extend: {
263
268
  colors: {
@@ -313,12 +318,34 @@ module.exports = {
313
318
  lg: 'var(--radius-lg)',
314
319
  xl: 'var(--radius-xl)',
315
320
  full: 'var(--radius-full)',
321
+ // Geometry-axis intent overlay. Codemod targets these for `rounded-*` by component intent:
322
+ // Card / containers → rounded-container
323
+ // Button / interactive → rounded-interactive
324
+ // Avatar / Badge → rounded-pill
325
+ container: 'var(--radius-container, var(--radius-md))',
326
+ interactive: 'var(--radius-interactive, var(--radius-md))',
327
+ pill: 'var(--radius-pill, var(--radius-full))',
328
+ },
329
+ borderWidth: {
330
+ // Geometry-axis border rhythm with intent. Existing literal `border` / `border-2` keep working.
331
+ hairline: 'var(--border-hairline, 1px)',
332
+ standard: 'var(--border-standard, var(--border-width, 1px))',
333
+ heavy: 'var(--border-heavy, var(--border-width-thick, 2px))',
316
334
  },
317
335
  boxShadow: {
318
336
  sm: 'var(--shadow-sm)',
319
337
  DEFAULT: 'var(--shadow-main)',
320
338
  lg: 'var(--shadow-lg)',
321
339
  inner: 'var(--shadow-inner)',
340
+ // Elevation-axis per-layer intent. Codemod targets:
341
+ // Card → shadow-elevation-card
342
+ // Popover / Tooltip → shadow-elevation-popover
343
+ // Dialog → shadow-elevation-dialog
344
+ // Toast → shadow-elevation-toast
345
+ 'elevation-card': 'var(--elevation-card, var(--shadow-sm))',
346
+ 'elevation-popover': 'var(--elevation-popover, var(--shadow-main))',
347
+ 'elevation-dialog': 'var(--elevation-dialog, var(--shadow-lg))',
348
+ 'elevation-toast': 'var(--elevation-toast, var(--shadow-main))',
322
349
  },
323
350
  fontWeight: {
324
351
  normal: 'var(--font-weight-normal, 400)',
@@ -326,10 +353,98 @@ module.exports = {
326
353
  bold: 'var(--font-weight-bold, 600)',
327
354
  },
328
355
  transitionDuration: {
329
- fast: 'var(--transition-fast, 150ms)',
330
- normal: 'var(--transition-normal, 250ms)',
331
- slow: 'var(--transition-slow, 400ms)',
356
+ // Existing aliases retained; resolve through the Motion-axis tokens
357
+ // with the previous `--transition-*` values as fallback so themes that
358
+ // don't define `--duration-*` keep their pre-Layer-1 motion.
359
+ fast: 'var(--duration-fast, var(--transition-fast, 150ms))',
360
+ normal: 'var(--duration-normal, var(--transition-normal, 250ms))',
361
+ slow: 'var(--duration-slow, var(--transition-slow, 400ms))',
362
+ instant: 'var(--duration-instant, 0ms)',
363
+ dramatic: 'var(--duration-dramatic, 600ms)',
364
+ },
365
+ transitionTimingFunction: {
366
+ // Motion-axis easing palette. `standard` falls back to the existing
367
+ // `--transition-timing` so backward compat is preserved.
368
+ linear: 'var(--easing-linear, linear)',
369
+ standard: 'var(--easing-standard, var(--transition-timing, cubic-bezier(0.4, 0, 0.2, 1)))',
370
+ emphasized: 'var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1))',
371
+ spring: 'var(--easing-spring, cubic-bezier(0.34, 1.56, 0.64, 1))',
372
+ },
373
+ // Density-axis spacing scale. Tailwind's default `p-3` / `gap-4` resolve
374
+ // here so existing classes keep working — but now read from tokens.
375
+ // Defaults match Tailwind's default (12px / 16px / 24px for 3 / 4 / 6),
376
+ // so post-migration visual output is identical until a theme overrides.
377
+ spacing: {
378
+ 0: 'var(--space-0, 0px)',
379
+ 1: 'var(--space-1, 4px)',
380
+ 2: 'var(--space-2, 8px)',
381
+ 3: 'var(--space-3, 12px)',
382
+ 4: 'var(--space-4, 16px)',
383
+ 5: 'var(--space-5, 20px)',
384
+ 6: 'var(--space-6, 24px)',
385
+ 7: 'var(--space-7, 28px)',
386
+ 8: 'var(--space-8, 32px)',
387
+ 9: 'var(--space-9, 36px)',
388
+ 10: 'var(--space-10, 40px)',
389
+ 11: 'var(--space-11, 44px)',
390
+ 12: 'var(--space-12, 48px)',
391
+ // Component-intent spacing (codemod targets for padding-by-intent)
392
+ 'card-sm': 'var(--card-padding-sm, 12px)',
393
+ 'card-md': 'var(--card-padding-md, 16px)',
394
+ 'card-lg': 'var(--card-padding-lg, 24px)',
395
+ 'dialog': 'var(--dialog-padding, 24px)',
396
+ 'section': 'var(--section-gap, 32px)',
397
+ },
398
+ // Type-axis size scale. Each step pairs a size with a line-height,
399
+ // both read from tokens with Tailwind-default fallbacks so existing
400
+ // `text-sm` resolves to the same 14px/20px until a theme overrides.
401
+ fontSize: {
402
+ xs: ['var(--text-xs, 12px)', { lineHeight: 'var(--leading-xs, 16px)' }],
403
+ sm: ['var(--text-sm, 14px)', { lineHeight: 'var(--leading-sm, 20px)' }],
404
+ base: ['var(--text-base, 16px)', { lineHeight: 'var(--leading-base, 24px)' }],
405
+ lg: ['var(--text-lg, 18px)', { lineHeight: 'var(--leading-lg, 28px)' }],
406
+ xl: ['var(--text-xl, 20px)', { lineHeight: 'var(--leading-xl, 28px)' }],
407
+ '2xl': ['var(--text-2xl, 24px)', { lineHeight: 'var(--leading-2xl, 32px)' }],
408
+ '3xl': ['var(--text-3xl, 30px)', { lineHeight: 'var(--leading-3xl, 36px)' }],
409
+ '4xl': ['var(--text-4xl, 36px)', { lineHeight: 'var(--leading-4xl, 40px)' }],
410
+ 'display-1': ['var(--text-display-1, 48px)', { lineHeight: 'var(--leading-display-1, 52px)' }],
411
+ 'display-2': ['var(--text-display-2, 60px)', { lineHeight: 'var(--leading-display-2, 64px)' }],
412
+ },
413
+ // Density-axis per-element heights (codemod targets for `h-{px}` on
414
+ // buttons / inputs / rows / icons).
415
+ height: {
416
+ 'button-sm': 'var(--button-height-sm, 28px)',
417
+ 'button-md': 'var(--button-height-md, 36px)',
418
+ 'button-lg': 'var(--button-height-lg, 44px)',
419
+ 'input-sm': 'var(--input-height-sm, 28px)',
420
+ 'input-md': 'var(--input-height-md, 36px)',
421
+ 'input-lg': 'var(--input-height-lg, 44px)',
422
+ 'row-compact': 'var(--row-height-compact, 32px)',
423
+ 'row-normal': 'var(--row-height-normal, 40px)',
424
+ 'row-spacious': 'var(--row-height-spacious, 48px)',
425
+ 'icon-default': 'var(--icon-default-size, 16px)',
426
+ },
427
+ minHeight: {
428
+ 'button-sm': 'var(--button-height-sm, 28px)',
429
+ 'button-md': 'var(--button-height-md, 36px)',
430
+ 'button-lg': 'var(--button-height-lg, 44px)',
431
+ 'input-sm': 'var(--input-height-sm, 28px)',
432
+ 'input-md': 'var(--input-height-md, 36px)',
433
+ 'input-lg': 'var(--input-height-lg, 44px)',
434
+ 'row-compact': 'var(--row-height-compact, 32px)',
435
+ 'row-normal': 'var(--row-height-normal, 40px)',
436
+ 'row-spacious': 'var(--row-height-spacious, 48px)',
437
+ },
438
+ width: {
439
+ 'icon-default': 'var(--icon-default-size, 16px)',
332
440
  },
441
+ // Geometry-axis radius rhythm (codemod targets for `rounded-*` by intent).
442
+ // The existing sm/md/lg/xl/full keys above continue to work as-is.
443
+ // Codemod converts `rounded-md` on Card → `rounded-container`, on Button → `rounded-interactive`, on Avatar/Badge → `rounded-pill`.
444
+ // borderRadius is extended below alongside the existing radius tokens.
445
+ // Elevation-axis per-layer shadow mapping.
446
+ // Codemod converts `shadow-sm` on Card → `shadow-elevation-card`, etc.
447
+ // boxShadow is extended below.
333
448
  // Container-query breakpoints aligned 1:1 with Tailwind's default
334
449
  // viewport breakpoints (sm 640 / md 768 / lg 1024 / xl 1280 /
335
450
  // 2xl 1536). Lets components use `@lg/foo:hidden` as a drop-in
@@ -0,0 +1,198 @@
1
+ # Theme CSS Variable Contract
2
+
3
+ > Reference: `docs/Almadar_Std_Variations.md` §2 (Skin layer).
4
+ > Type counterpart: `@almadar/core` exports `ThemeTokens` with typed sub-interfaces — `DensityTokens`, `TypeScaleTokens`, `MotionTokens`, `IconographyTokens`, `ElevationTokens`, `GeometryTokens`.
5
+ > This file is **not loaded at runtime**. It is the authoritative reference both for theme authors and for the `_defaults.css` fallback layer in `themes/index.css`.
6
+
7
+ CSS custom properties are the runtime source of truth — every `@almadar/ui` component reads from these variables via Tailwind utility classes (mapped in `tailwind-preset.cjs`). A theme that omits a variable inherits a deterministic fallback from the `:root` block declared in `themes/index.css`; the fallbacks are calibrated to preserve the visual baseline of the 18 pre-Layer-1 themes.
8
+
9
+ A "truly unique" theme overrides variables across **all** axes below — not just `--color-*`. Two themes that diverge only on color will feel like the same product in two paint jobs.
10
+
11
+ ## Existing variables (preserved unchanged)
12
+
13
+ These were defined by every pre-Layer-1 theme and continue to be the canonical paint surface. Backfill agents MUST NOT change existing values; they only append the new axis variables below.
14
+
15
+ ### Color
16
+ `--color-primary`, `--color-primary-hover`, `--color-primary-foreground`,
17
+ `--color-secondary`, `--color-secondary-hover`, `--color-secondary-foreground`,
18
+ `--color-accent`, `--color-accent-foreground`,
19
+ `--color-muted`, `--color-muted-foreground`,
20
+ `--color-background`, `--color-foreground`,
21
+ `--color-card`, `--color-card-foreground`,
22
+ `--color-surface`, `--color-border`, `--color-input`, `--color-ring`,
23
+ `--color-error`, `--color-error-foreground`,
24
+ `--color-success`, `--color-success-foreground`,
25
+ `--color-warning`, `--color-warning-foreground`,
26
+ `--color-info`, `--color-info-foreground`.
27
+
28
+ Optional table-specific (defined by some themes, fall back to neutrals when absent): `--color-table-header`, `--color-table-border`, `--color-table-row-hover`, `--color-surface-hover`, `--color-border-hover`, `--color-placeholder`.
29
+
30
+ ### Radius (existing — Geometry axis below adds intent overlay)
31
+ `--radius-none`, `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`, `--radius-full`.
32
+
33
+ ### Border width (existing — Geometry axis adds intent overlay)
34
+ `--border-width`, `--border-width-thin`, `--border-width-thick`.
35
+
36
+ ### Shadow (existing — Elevation axis adds intent overlay)
37
+ `--shadow-none`, `--shadow-sm`, `--shadow-main`, `--shadow-lg`, `--shadow-inner`, `--shadow-hover`, `--shadow-active`.
38
+
39
+ ### Typography (existing — TypeScale axis adds family triplet + scale)
40
+ `--font-family`, `--font-weight-normal`, `--font-weight-medium`, `--font-weight-bold`, `--line-height`, `--letter-spacing`.
41
+
42
+ ### Icon (existing — Iconography axis adds family selector)
43
+ `--icon-stroke-width`, `--icon-color`.
44
+
45
+ ### Transitions (existing — Motion axis adds named palette)
46
+ `--transition-fast`, `--transition-normal`, `--transition-slow`, `--transition-timing`.
47
+
48
+ ### Interaction (existing)
49
+ `--hover-scale`, `--hover-translate-y`, `--hover-translate-x`,
50
+ `--active-scale`, `--active-translate-y`,
51
+ `--focus-ring-width`, `--focus-ring-offset`, `--focus-ring-color`.
52
+
53
+ ---
54
+
55
+ ## New variables (added in Layer 1)
56
+
57
+ Every theme — backfilled or new — defines the variables below. The defaults column shows what the `_defaults.css` `:root` block declares; backfill agents copy these into each existing theme block so post-migration visual output stays identical. New-theme authors override per their personality brief.
58
+
59
+ ### Density axis — `--space-*` scale + per-element heights/paddings
60
+
61
+ Spacing scale (12 steps; current Tailwind defaults shown to preserve `p-3 / p-4 / p-6` parity):
62
+
63
+ | Variable | Default | Tailwind equivalent |
64
+ |---|---|---|
65
+ | `--space-0` | `0px` | `0` |
66
+ | `--space-1` | `4px` | `1` |
67
+ | `--space-2` | `8px` | `2` |
68
+ | `--space-3` | `12px` | `3` |
69
+ | `--space-4` | `16px` | `4` |
70
+ | `--space-5` | `20px` | `5` |
71
+ | `--space-6` | `24px` | `6` |
72
+ | `--space-7` | `28px` | `7` |
73
+ | `--space-8` | `32px` | `8` |
74
+ | `--space-9` | `36px` | `9` |
75
+ | `--space-10` | `40px` | `10` |
76
+ | `--space-11` | `44px` | `11` |
77
+ | `--space-12` | `48px` | `12` |
78
+
79
+ Per-element heights:
80
+
81
+ | Variable | Default | Used by |
82
+ |---|---|---|
83
+ | `--button-height-sm` | `28px` | Button size="sm" |
84
+ | `--button-height-md` | `36px` | Button size="md" |
85
+ | `--button-height-lg` | `44px` | Button size="lg" |
86
+ | `--input-height-sm` | `28px` | Input size="sm" |
87
+ | `--input-height-md` | `36px` | Input size="md" |
88
+ | `--input-height-lg` | `44px` | Input size="lg" |
89
+ | `--row-height-compact` | `32px` | Table dense rows |
90
+ | `--row-height-normal` | `40px` | Table default rows |
91
+ | `--row-height-spacious` | `48px` | Table comfortable rows |
92
+
93
+ Per-element padding:
94
+
95
+ | Variable | Default | Used by |
96
+ |---|---|---|
97
+ | `--card-padding-sm` | `12px` | Card padding="sm" (matches `p-3`) |
98
+ | `--card-padding-md` | `16px` | Card padding="md" (matches `p-4`) |
99
+ | `--card-padding-lg` | `24px` | Card padding="lg" (matches `p-6`) |
100
+ | `--dialog-padding` | `24px` | Dialog body |
101
+ | `--section-gap` | `32px` | Section-to-section vertical rhythm |
102
+
103
+ ### Type axis — family triplet + scale + intent mapping
104
+
105
+ Family slots (the existing `--font-family` aliases to `--font-family-body` for backward compat):
106
+
107
+ | Variable | Default |
108
+ |---|---|
109
+ | `--font-family-display` | `var(--font-family)` |
110
+ | `--font-family-body` | `var(--font-family)` |
111
+ | `--font-family-mono` | `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` |
112
+
113
+ Size scale + paired line-heights (defaults match Tailwind's default `text-*` scale so existing `text-sm` resolves to identical px):
114
+
115
+ | Size key | `--text-*` default | `--leading-*` default |
116
+ |---|---|---|
117
+ | `xs` | `12px` | `16px` |
118
+ | `sm` | `14px` | `20px` |
119
+ | `base` | `16px` | `24px` |
120
+ | `lg` | `18px` | `28px` |
121
+ | `xl` | `20px` | `28px` |
122
+ | `2xl` | `24px` | `32px` |
123
+ | `3xl` | `30px` | `36px` |
124
+ | `4xl` | `36px` | `40px` |
125
+ | `display-1` | `48px` | `52px` |
126
+ | `display-2` | `60px` | `64px` |
127
+
128
+ Intent mapping — three vars per intent (`size`, `weight`, `leading`). Defaults are calibrated against current component usage (Card title `text-lg` → `heading-major`; subtitle `text-sm` muted → `caption`):
129
+
130
+ | Intent | `--intent-*-size` | `--intent-*-weight` | `--intent-*-leading` |
131
+ |---|---|---|---|
132
+ | `heading-major` | `var(--text-2xl)` | `var(--font-weight-bold)` | `var(--leading-2xl)` |
133
+ | `heading-minor` | `var(--text-lg)` | `var(--font-weight-bold)` | `var(--leading-lg)` |
134
+ | `body-emphasis` | `var(--text-base)` | `var(--font-weight-medium)` | `var(--leading-base)` |
135
+ | `body-default` | `var(--text-sm)` | `var(--font-weight-normal)` | `var(--leading-sm)` |
136
+ | `body-quiet` | `var(--text-sm)` | `var(--font-weight-normal)` | `var(--leading-sm)` |
137
+ | `caption` | `var(--text-xs)` | `var(--font-weight-normal)` | `var(--leading-xs)` |
138
+ | `numeric` | `var(--text-sm)` | `var(--font-weight-medium)` | `var(--leading-sm)` |
139
+
140
+ ### Motion axis — duration palette + easing palette
141
+
142
+ Duration palette (existing `--transition-fast/normal/slow` alias to `--duration-fast/normal/slow`):
143
+
144
+ | Variable | Default |
145
+ |---|---|
146
+ | `--duration-instant` | `0ms` |
147
+ | `--duration-fast` | `var(--transition-fast)` |
148
+ | `--duration-normal` | `var(--transition-normal)` |
149
+ | `--duration-slow` | `var(--transition-slow)` |
150
+ | `--duration-dramatic` | `600ms` |
151
+
152
+ Easing palette (existing `--transition-timing` aliases to `--easing-standard`):
153
+
154
+ | Variable | Default |
155
+ |---|---|
156
+ | `--easing-linear` | `linear` |
157
+ | `--easing-standard` | `var(--transition-timing)` |
158
+ | `--easing-emphasized` | `cubic-bezier(0.2, 0, 0, 1)` |
159
+ | `--easing-spring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` |
160
+
161
+ ### Iconography axis
162
+
163
+ | Variable | Default | Notes |
164
+ |---|---|---|
165
+ | `--icon-family` | `lucide` | String identifier consumed by the icon resolver. Values: `lucide` \| `phosphor-outline` \| `phosphor-fill` \| `phosphor-duotone` \| `tabler` \| `fa-solid`. |
166
+ | `--icon-default-size` | `16px` | Default icon px size when component doesn't override. |
167
+
168
+ `--icon-stroke-width` and `--icon-color` continue from the existing surface.
169
+
170
+ ### Elevation axis — per-layer mapping
171
+
172
+ | Variable | Default |
173
+ |---|---|
174
+ | `--elevation-card` | `var(--shadow-sm)` |
175
+ | `--elevation-popover` | `var(--shadow-main)` |
176
+ | `--elevation-dialog` | `var(--shadow-lg)` |
177
+ | `--elevation-toast` | `var(--shadow-main)` |
178
+
179
+ ### Geometry axis — radius rhythm + border-width rhythm with intent
180
+
181
+ | Variable | Default |
182
+ |---|---|
183
+ | `--radius-container` | `var(--radius-md)` |
184
+ | `--radius-interactive` | `var(--radius-md)` |
185
+ | `--radius-pill` | `var(--radius-full)` |
186
+ | `--border-hairline` | `1px` |
187
+ | `--border-standard` | `var(--border-width)` |
188
+ | `--border-heavy` | `var(--border-width-thick)` |
189
+
190
+ ---
191
+
192
+ ## Authoring rules
193
+
194
+ 1. **Backfill agents** (B1, B2, B3): copy the defaults above verbatim into each existing theme's light and dark blocks. Do not change any pre-existing variable. The migration must produce pixel-identical output to today for every pre-Layer-1 theme.
195
+ 2. **New-theme agents** (B4–B8): define every variable above. Pick values that express the personality brief — compact density, editorial type, sharp geometry, dramatic motion, filled iconography, etc. Defaults are NOT a fallback for new themes; new themes are the proof that the axis surface works.
196
+ 3. **No raw pixel literals in components**. All dimensional values flow through these tokens or through Tailwind utilities backed by them in `tailwind-preset.cjs`. Components that hardcode `text-[10px]` or `min-w-[44px]` are migration targets.
197
+ 4. **Icon family swap is runtime-resolved**. `--icon-family` is read by the icon resolver in `@almadar/ui`; setting the variable in a theme does not require component edits.
198
+ 5. **Wireframe and trait-wars exceptions**. Wireframe's `--easing-standard` is `linear` not the default cubic-bezier; trait-wars's iconography may remain `lucide` even if its visual personality diverges. Document any per-theme override at the top of the theme file.
@@ -97,6 +97,112 @@
97
97
  --focus-ring-width: 2px;
98
98
  --focus-ring-offset: 2px;
99
99
  --focus-ring-color: #14b8a6;
100
+
101
+ /* Layer 1 — backfill (preserves current visual output, see themes/_contract.md) */
102
+ /* Density axis — spacing scale */
103
+ --space-0: 0px;
104
+ --space-1: 4px;
105
+ --space-2: 8px;
106
+ --space-3: 12px;
107
+ --space-4: 16px;
108
+ --space-5: 20px;
109
+ --space-6: 24px;
110
+ --space-7: 28px;
111
+ --space-8: 32px;
112
+ --space-9: 36px;
113
+ --space-10: 40px;
114
+ --space-11: 44px;
115
+ --space-12: 48px;
116
+ /* Density axis — per-element heights */
117
+ --button-height-sm: 28px;
118
+ --button-height-md: 36px;
119
+ --button-height-lg: 44px;
120
+ --input-height-sm: 28px;
121
+ --input-height-md: 36px;
122
+ --input-height-lg: 44px;
123
+ --row-height-compact: 32px;
124
+ --row-height-normal: 40px;
125
+ --row-height-spacious: 48px;
126
+ /* Density axis — per-element padding */
127
+ --card-padding-sm: 12px;
128
+ --card-padding-md: 16px;
129
+ --card-padding-lg: 24px;
130
+ --dialog-padding: 24px;
131
+ --section-gap: 32px;
132
+ /* Type axis — family triplet (aliases existing --font-family for backward compat) */
133
+ --font-family-display: var(--font-family);
134
+ --font-family-body: var(--font-family);
135
+ --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
136
+ /* Type axis — size scale */
137
+ --text-xs: 12px;
138
+ --leading-xs: 16px;
139
+ --text-sm: 14px;
140
+ --leading-sm: 20px;
141
+ --text-base: 16px;
142
+ --leading-base: 24px;
143
+ --text-lg: 18px;
144
+ --leading-lg: 28px;
145
+ --text-xl: 20px;
146
+ --leading-xl: 28px;
147
+ --text-2xl: 24px;
148
+ --leading-2xl: 32px;
149
+ --text-3xl: 30px;
150
+ --leading-3xl: 36px;
151
+ --text-4xl: 36px;
152
+ --leading-4xl: 40px;
153
+ --text-display-1: 48px;
154
+ --leading-display-1: 52px;
155
+ --text-display-2: 60px;
156
+ --leading-display-2: 64px;
157
+ /* Type axis — intent mapping (calibrated to existing component usage) */
158
+ --intent-heading-major-size: var(--text-2xl);
159
+ --intent-heading-major-weight: var(--font-weight-bold);
160
+ --intent-heading-major-leading: var(--leading-2xl);
161
+ --intent-heading-minor-size: var(--text-lg);
162
+ --intent-heading-minor-weight: var(--font-weight-bold);
163
+ --intent-heading-minor-leading: var(--leading-lg);
164
+ --intent-body-emphasis-size: var(--text-base);
165
+ --intent-body-emphasis-weight: var(--font-weight-medium);
166
+ --intent-body-emphasis-leading: var(--leading-base);
167
+ --intent-body-default-size: var(--text-sm);
168
+ --intent-body-default-weight: var(--font-weight-normal);
169
+ --intent-body-default-leading: var(--leading-sm);
170
+ --intent-body-quiet-size: var(--text-sm);
171
+ --intent-body-quiet-weight: var(--font-weight-normal);
172
+ --intent-body-quiet-leading: var(--leading-sm);
173
+ --intent-caption-size: var(--text-xs);
174
+ --intent-caption-weight: var(--font-weight-normal);
175
+ --intent-caption-leading: var(--leading-xs);
176
+ --intent-numeric-size: var(--text-sm);
177
+ --intent-numeric-weight: var(--font-weight-medium);
178
+ --intent-numeric-leading: var(--leading-sm);
179
+ /* Motion axis — duration palette (aliases existing --transition-* for backward compat) */
180
+ --duration-instant: 0ms;
181
+ --duration-fast: var(--transition-fast);
182
+ --duration-normal: var(--transition-normal);
183
+ --duration-slow: var(--transition-slow);
184
+ --duration-dramatic: 600ms;
185
+ /* Motion axis — easing palette (aliases existing --transition-timing for backward compat) */
186
+ --easing-linear: linear;
187
+ --easing-standard: var(--transition-timing);
188
+ --easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
189
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
190
+ /* Iconography axis */
191
+ --icon-family: lucide;
192
+ --icon-default-size: 16px;
193
+ /* Elevation axis — per-layer mapping */
194
+ --elevation-card: var(--shadow-sm);
195
+ --elevation-popover: var(--shadow-main);
196
+ --elevation-dialog: var(--shadow-lg);
197
+ --elevation-toast: var(--shadow-main);
198
+ /* Geometry axis — radius rhythm with intent */
199
+ --radius-container: var(--radius-md);
200
+ --radius-interactive: var(--radius-md);
201
+ --radius-pill: var(--radius-full);
202
+ /* Geometry axis — border-width rhythm */
203
+ --border-hairline: 1px;
204
+ --border-standard: var(--border-width);
205
+ --border-heavy: var(--border-width-thick);
100
206
  }
101
207
 
102
208
  /* ==========================================================================
@@ -194,4 +300,110 @@
194
300
  --focus-ring-width: 2px;
195
301
  --focus-ring-offset: 2px;
196
302
  --focus-ring-color: #14b8a6;
303
+
304
+ /* Layer 1 — backfill (preserves current visual output, see themes/_contract.md) */
305
+ /* Density axis — spacing scale */
306
+ --space-0: 0px;
307
+ --space-1: 4px;
308
+ --space-2: 8px;
309
+ --space-3: 12px;
310
+ --space-4: 16px;
311
+ --space-5: 20px;
312
+ --space-6: 24px;
313
+ --space-7: 28px;
314
+ --space-8: 32px;
315
+ --space-9: 36px;
316
+ --space-10: 40px;
317
+ --space-11: 44px;
318
+ --space-12: 48px;
319
+ /* Density axis — per-element heights */
320
+ --button-height-sm: 28px;
321
+ --button-height-md: 36px;
322
+ --button-height-lg: 44px;
323
+ --input-height-sm: 28px;
324
+ --input-height-md: 36px;
325
+ --input-height-lg: 44px;
326
+ --row-height-compact: 32px;
327
+ --row-height-normal: 40px;
328
+ --row-height-spacious: 48px;
329
+ /* Density axis — per-element padding */
330
+ --card-padding-sm: 12px;
331
+ --card-padding-md: 16px;
332
+ --card-padding-lg: 24px;
333
+ --dialog-padding: 24px;
334
+ --section-gap: 32px;
335
+ /* Type axis — family triplet (aliases existing --font-family for backward compat) */
336
+ --font-family-display: var(--font-family);
337
+ --font-family-body: var(--font-family);
338
+ --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
339
+ /* Type axis — size scale */
340
+ --text-xs: 12px;
341
+ --leading-xs: 16px;
342
+ --text-sm: 14px;
343
+ --leading-sm: 20px;
344
+ --text-base: 16px;
345
+ --leading-base: 24px;
346
+ --text-lg: 18px;
347
+ --leading-lg: 28px;
348
+ --text-xl: 20px;
349
+ --leading-xl: 28px;
350
+ --text-2xl: 24px;
351
+ --leading-2xl: 32px;
352
+ --text-3xl: 30px;
353
+ --leading-3xl: 36px;
354
+ --text-4xl: 36px;
355
+ --leading-4xl: 40px;
356
+ --text-display-1: 48px;
357
+ --leading-display-1: 52px;
358
+ --text-display-2: 60px;
359
+ --leading-display-2: 64px;
360
+ /* Type axis — intent mapping (calibrated to existing component usage) */
361
+ --intent-heading-major-size: var(--text-2xl);
362
+ --intent-heading-major-weight: var(--font-weight-bold);
363
+ --intent-heading-major-leading: var(--leading-2xl);
364
+ --intent-heading-minor-size: var(--text-lg);
365
+ --intent-heading-minor-weight: var(--font-weight-bold);
366
+ --intent-heading-minor-leading: var(--leading-lg);
367
+ --intent-body-emphasis-size: var(--text-base);
368
+ --intent-body-emphasis-weight: var(--font-weight-medium);
369
+ --intent-body-emphasis-leading: var(--leading-base);
370
+ --intent-body-default-size: var(--text-sm);
371
+ --intent-body-default-weight: var(--font-weight-normal);
372
+ --intent-body-default-leading: var(--leading-sm);
373
+ --intent-body-quiet-size: var(--text-sm);
374
+ --intent-body-quiet-weight: var(--font-weight-normal);
375
+ --intent-body-quiet-leading: var(--leading-sm);
376
+ --intent-caption-size: var(--text-xs);
377
+ --intent-caption-weight: var(--font-weight-normal);
378
+ --intent-caption-leading: var(--leading-xs);
379
+ --intent-numeric-size: var(--text-sm);
380
+ --intent-numeric-weight: var(--font-weight-medium);
381
+ --intent-numeric-leading: var(--leading-sm);
382
+ /* Motion axis — duration palette (aliases existing --transition-* for backward compat) */
383
+ --duration-instant: 0ms;
384
+ --duration-fast: var(--transition-fast);
385
+ --duration-normal: var(--transition-normal);
386
+ --duration-slow: var(--transition-slow);
387
+ --duration-dramatic: 600ms;
388
+ /* Motion axis — easing palette (aliases existing --transition-timing for backward compat) */
389
+ --easing-linear: linear;
390
+ --easing-standard: var(--transition-timing);
391
+ --easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
392
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
393
+ /* Iconography axis */
394
+ --icon-family: lucide;
395
+ --icon-default-size: 16px;
396
+ /* Elevation axis — per-layer mapping */
397
+ --elevation-card: var(--shadow-sm);
398
+ --elevation-popover: var(--shadow-main);
399
+ --elevation-dialog: var(--shadow-lg);
400
+ --elevation-toast: var(--shadow-main);
401
+ /* Geometry axis — radius rhythm with intent */
402
+ --radius-container: var(--radius-md);
403
+ --radius-interactive: var(--radius-md);
404
+ --radius-pill: var(--radius-full);
405
+ /* Geometry axis — border-width rhythm */
406
+ --border-hairline: 1px;
407
+ --border-standard: var(--border-width);
408
+ --border-heavy: var(--border-width-thick);
197
409
  }