@ttoss/fsl-theme 1.1.22 → 2.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 (66) hide show
  1. package/README.md +14 -12
  2. package/dist/{Types-BiBa17RL.d.cts → Types-BkaxtsMv.d.cts} +61 -13
  3. package/dist/{Types-BiBa17RL.d.mts → Types-BkaxtsMv.d.mts} +61 -13
  4. package/dist/{baseBundle-BWbICg4p.cjs → baseBundle-DdRSa5mf.cjs} +3 -2
  5. package/dist/{baseBundle-DiYlHNG5.mjs → baseBundle-R7WyjyTw.mjs} +3 -2
  6. package/dist/{createTheme-BHGc1IFI.cjs → createTheme--UFgVlEn.cjs} +34 -3
  7. package/dist/{createTheme-C1r5nJco.mjs → createTheme-B6tVIikC.mjs} +29 -4
  8. package/dist/css.cjs +2 -2
  9. package/dist/css.d.cts +1 -1
  10. package/dist/css.d.mts +1 -1
  11. package/dist/css.mjs +2 -2
  12. package/dist/dataviz/index.cjs +2 -2
  13. package/dist/dataviz/index.d.cts +1 -1
  14. package/dist/dataviz/index.d.mts +1 -1
  15. package/dist/dataviz/index.mjs +1 -1
  16. package/dist/dtcg.cjs +26 -9
  17. package/dist/dtcg.d.cts +17 -4
  18. package/dist/dtcg.d.mts +17 -4
  19. package/dist/dtcg.mjs +26 -9
  20. package/dist/{helpers-CIqeJ8yd.cjs → helpers-BU40JvFA.cjs} +27 -5
  21. package/dist/{helpers-Dz7NfNK6.mjs → helpers-BXaKe2s3.mjs} +27 -5
  22. package/dist/{index-BuszkWMy.d.mts → index-7YL90Xqs.d.cts} +9 -12
  23. package/dist/{index-Djro6G4e.d.cts → index-BmCTIOzh.d.mts} +9 -12
  24. package/dist/index.cjs +2 -8
  25. package/dist/index.d.cts +4 -4
  26. package/dist/index.d.mts +4 -4
  27. package/dist/index.mjs +2 -5
  28. package/dist/{react-D2J482kx.d.mts → react-C8JrZf-b.d.mts} +14 -2
  29. package/dist/{react-BKLEzIN8.cjs → react-DLB8nrXQ.cjs} +18 -2
  30. package/dist/{react-BXqSYnmk.d.cts → react-Dt7vGAVG.d.cts} +14 -2
  31. package/dist/react.cjs +1 -1
  32. package/dist/react.d.cts +1 -1
  33. package/dist/react.d.mts +1 -1
  34. package/dist/react.mjs +18 -2
  35. package/dist/runtime-entry.cjs +1 -1
  36. package/dist/runtime-entry.mjs +1 -1
  37. package/dist/{ssrScript-DfmopH-w.mjs → ssrScript-DNLlLDps.mjs} +1 -1
  38. package/dist/{ssrScript-D3mYscp1.cjs → ssrScript-MF5SIONH.cjs} +1 -1
  39. package/dist/themes/bruttal.cjs +19 -4
  40. package/dist/themes/bruttal.d.cts +1 -1
  41. package/dist/themes/bruttal.d.mts +1 -1
  42. package/dist/themes/bruttal.mjs +19 -4
  43. package/dist/{toCssVars-DDowz4wU.cjs → toCssVars-BseqmMcT.cjs} +27 -2
  44. package/dist/{toCssVars-CeckLjW1.mjs → toCssVars-C-9yIZDR.mjs} +27 -2
  45. package/dist/{tokenRegistry-DolhB5Vf.mjs → tokenRegistry-DviyqDeE.mjs} +19 -28
  46. package/dist/{tokenRegistry-B9zMRjpL.cjs → tokenRegistry-f_Bx_BAN.cjs} +19 -28
  47. package/dist/vars.cjs +3 -3
  48. package/dist/vars.d.cts +1 -1
  49. package/dist/vars.d.mts +1 -1
  50. package/dist/vars.mjs +3 -3
  51. package/dist/{withDataviz-CrgXLR5A.mjs → withDataviz-BvpZJnFx.mjs} +2 -2
  52. package/dist/{withDataviz-BzZwFV2e.cjs → withDataviz-CYRf4exT.cjs} +2 -2
  53. package/llms.txt +5 -5
  54. package/package.json +1 -1
  55. package/dist/themes/corporate.cjs +0 -34
  56. package/dist/themes/corporate.d.cts +0 -3
  57. package/dist/themes/corporate.d.mts +0 -3
  58. package/dist/themes/corporate.mjs +0 -31
  59. package/dist/themes/oca.cjs +0 -34
  60. package/dist/themes/oca.d.cts +0 -3
  61. package/dist/themes/oca.d.mts +0 -3
  62. package/dist/themes/oca.mjs +0 -31
  63. package/dist/themes/ventures.cjs +0 -34
  64. package/dist/themes/ventures.d.cts +0 -3
  65. package/dist/themes/ventures.d.mts +0 -3
  66. package/dist/themes/ventures.mjs +0 -31
package/README.md CHANGED
@@ -10,15 +10,15 @@ pnpm add @ttoss/fsl-theme
10
10
 
11
11
  ## Entry points
12
12
 
13
- | Import | Exports |
14
- | :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | `@ttoss/fsl-theme` | `createTheme`, `darkAlternate`, built-in themes (`bruttal`, `corporate`, `oca`, `ventures`), types (`ThemeTokens`, `ThemeBundle`, `SemanticTokens`, `ModeOverride`, `DeepPartial`, `ThemeMode`) |
16
- | `@ttoss/fsl-theme/react` | `ThemeProvider`, `ThemeHead`, `ThemeScript`, `ThemeStyles`, `useColorMode`, `useTokens`, `useResolvedTokens` |
17
- | `@ttoss/fsl-theme/dataviz` | `withDataviz`, `useDatavizTokens` |
18
- | `@ttoss/fsl-theme/css` | `getThemeStylesContent`, `toCssVars`, `toCssVarName`, `toFlatTokens` |
19
- | `@ttoss/fsl-theme/vars` | `vars`, `buildVarsMap`, type `CssVarsMap` |
20
- | `@ttoss/fsl-theme/dtcg` | `toDTCG` (W3C Design Tokens format) |
21
- | `@ttoss/fsl-theme/runtime` | `createThemeRuntime`, `getThemeScriptContent` |
13
+ | Import | Exports |
14
+ | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `@ttoss/fsl-theme` | `createTheme`, `baseTheme`, `darkAlternate`, built-in theme (`bruttal`), types (`ThemeTokens`, `ThemeBundle`, `SemanticTokens`, `ModeOverride`, `DeepPartial`, `ThemeMode`) |
16
+ | `@ttoss/fsl-theme/react` | `ThemeProvider`, `ThemeHead`, `ThemeScript`, `ThemeStyles`, `useColorMode`, `useTokens`, `useResolvedTokens` |
17
+ | `@ttoss/fsl-theme/dataviz` | `withDataviz`, `useDatavizTokens` |
18
+ | `@ttoss/fsl-theme/css` | `getThemeStylesContent`, `toCssVars`, `toCssVarName`, `toFlatTokens` |
19
+ | `@ttoss/fsl-theme/vars` | `vars`, `buildVarsMap`, type `CssVarsMap` |
20
+ | `@ttoss/fsl-theme/dtcg` | `toDTCG` (W3C Design Tokens format) |
21
+ | `@ttoss/fsl-theme/runtime` | `createThemeRuntime`, `getThemeScriptContent` |
22
22
 
23
23
  ## Token architecture
24
24
 
@@ -53,7 +53,7 @@ One entry per semantic family. Use `vars.*` for typed CSS variable references; u
53
53
  | | `semantic.border.outline.{surface,control,selected}` | `{width, style}` |
54
54
  | focus | `semantic.focus.ring` | `{width, style, color}` |
55
55
  | elevation | `semantic.elevation.surface.{flat,raised,overlay,blocking}` | CSS box-shadow |
56
- | | `semantic.elevation.tonal.{flat,raised,overlay,blocking}` | CSS color (optional) |
56
+ | | `semantic.elevation.tonal.{raised,overlay,blocking}` | CSS color (optional) |
57
57
  | opacity | `semantic.opacity.{scrim,loading,disabled}` | number in (0, 1) |
58
58
  | overlay | `semantic.overlay.scrim` | CSS color with alpha |
59
59
  | motion | `semantic.motion.{feedback,emphasis,decorative}` | `{duration, easing}` |
@@ -290,7 +290,9 @@ For one-off custom keys, use `toCssVarName` from `@ttoss/fsl-theme/css` directly
290
290
 
291
291
  ### React 19 App Router (recommended)
292
292
 
293
- `ThemeProvider` with a `theme` prop uses React 19 style hoisting to inject CSS into `<head>` automatically. Only add `ThemeScript` for flash-prevention:
293
+ `ThemeProvider` with a `theme` prop uses React 19 style hoisting to inject CSS into `<head>` automatically. The injected `<style>` carries a stable `href` (`tt-theme-<themeId|root>`), so multiple providers or a re-render collapse to a single tag instead of duplicating. Only add `ThemeScript` for flash-prevention:
294
+
295
+ > **React 18:** auto-injection into `<head>` requires React 19 style hoisting. On React 18 the `<ThemeProvider>` `<style>` renders inline where the provider sits — use the `ThemeHead` / `ThemeStyles` path below to place CSS in `<head>` explicitly.
294
296
 
295
297
  ```tsx
296
298
  // app/layout.tsx
@@ -348,7 +350,7 @@ export default function RootLayout({
348
350
  }
349
351
  ```
350
352
 
351
- > **Warning:** Do not combine `<ThemeHead theme={...}>` with `<ThemeProvider theme={...}>`. Both inject CSS — `ThemeHead` via a plain `<style>` tag and `ThemeProvider` via React 19 style hoisting resulting in duplicate CSS in the document. Use one or the other.
353
+ > **Warning:** Do not pass `theme` to **both** `<ThemeHead>` and `<ThemeProvider>`. `ThemeHead` injects a plain `<style>`; `ThemeProvider` injects a React-19-hoisted `<style>`different mechanisms that do **not** dedup against each other, so you get duplicate CSS. Pass `theme` to the head component only; the body `<ThemeProvider>` (no `theme`) manages mode. (Multiple `<ThemeProvider theme={...}>` on React 19 _do_ dedup, via the shared `href`.)
352
354
 
353
355
  ## Dataviz extension
354
356
 
@@ -42,7 +42,7 @@ interface CoreDataviz {
42
42
  * dataviz.color.scale.diverging.neg3..pos3
43
43
  * dataviz.color.reference.baseline / target
44
44
  * dataviz.color.state.highlight / muted / selected
45
- * dataviz.color.status.missing / suppressed / not-applicable
45
+ * dataviz.color.status.missing / suppressed / notApplicable
46
46
  */
47
47
  interface SemanticDatavizColor {
48
48
  /**
@@ -82,7 +82,7 @@ interface SemanticDatavizColor {
82
82
  status: {
83
83
  /** Value is absent or unavailable. */missing: TokenRef; /** Value is withheld for confidentiality or publication rules. */
84
84
  suppressed: TokenRef; /** Value is structurally absent (not measured or not applicable). */
85
- na: TokenRef;
85
+ notApplicable: TokenRef;
86
86
  };
87
87
  }
88
88
  /**
@@ -440,7 +440,7 @@ interface FeedbackColorStates {
440
440
  interface ColorDimensionOf<S extends BaseColorStates> {
441
441
  /** Fills and surface backgrounds. Use for any colored area larger than a line. */
442
442
  background?: S;
443
- /** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.borders.*` instead — this dimension is colour only. */
443
+ /** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.border.*` instead — this dimension is colour only. */
444
444
  border?: S;
445
445
  /** Readable foreground — labels, paragraphs, and text-like icons that inherit `currentColor`. */
446
446
  text?: S;
@@ -587,7 +587,7 @@ interface SemanticElevation {
587
587
  * Tonal overlay tokens — optional surface color treatments paired with shadows
588
588
  * to preserve depth perception in dark or heavily-colored themes.
589
589
  *
590
- * Each token typically resolves to a color overlay (e.g., `color-mix`, rgba surface).
590
+ * Each token resolves to a color overlay (e.g., `color-mix`, rgba surface).
591
591
  * Omit when the product does not use tonal elevation.
592
592
  * When present, must cover the same strata that carry visible shadows.
593
593
  * @see elevation.md — "Surface + Shadow"
@@ -595,8 +595,8 @@ interface SemanticElevation {
595
595
  tonal?: {
596
596
  /**
597
597
  * Tonal surface treatment paired with `surface.raised`.
598
- * Use when the raised stratum needs an additional color overlay (typical in
599
- * dark themes where shadow alone is insufficient).
598
+ * Use when the raised stratum needs an additional color overlay dark
599
+ * themes where shadow alone is insufficient.
600
600
  * Pair with `surface.raised`; do not use without the matching shadow contract.
601
601
  */
602
602
  raised: CoreColorRef;
@@ -957,13 +957,13 @@ interface GapStackSteps {
957
957
  interface GapInlineSteps {
958
958
  /** Visual-only tight grouping (icon + label inside a single target). Never between focusable targets — use `separation.interactive.min`. */
959
959
  xs: CoreSpacingRef;
960
- /** Inline grouping same magnitude as `gap.stack.xs`. */
960
+ /** Tight inline grouping between related inline siblings. */
961
961
  sm: CoreSpacingRef;
962
- /** Looser inline grouping — same magnitude as `gap.stack.sm`. */
962
+ /** Default inline grouping — pick this when no other step applies. */
963
963
  md: CoreSpacingRef;
964
- /** Spacious inline grouping — same magnitude as `gap.stack.md`. */
964
+ /** Spacious inline grouping. */
965
965
  lg: CoreSpacingRef;
966
- /** Wide inline grouping — same magnitude as `gap.stack.lg`. */
966
+ /** Wide inline grouping — the loosest step before a group break. */
967
967
  xl: CoreSpacingRef;
968
968
  }
969
969
  interface SemanticSpacing {
@@ -1014,7 +1014,7 @@ interface SemanticSpacing {
1014
1014
  /**
1015
1015
  * Structural outer padding for page-level and section-level layout regions.
1016
1016
  * `page` and `section` may use a `clamp()` expression with embedded `{token.path}` refs
1017
- * (e.g. `clamp({core.space.4}, {core.space.6}, {core.space.12})`).
1017
+ * (e.g. `clamp({core.spacing.4}, {core.spacing.6}, {core.spacing.12})`).
1018
1018
  * Typed as `RawValue` to allow both simple refs and responsive clamp expressions.
1019
1019
  */
1020
1020
  gutter: {
@@ -1035,7 +1035,7 @@ interface SemanticSpacing {
1035
1035
  /**
1036
1036
  * Ergonomic separation between independently actionable targets in dense clusters.
1037
1037
  * May use a `clamp()` expression with an embedded `{token.path}` ref
1038
- * (e.g. `clamp(8px, {core.space.2}, 12px)`).
1038
+ * (e.g. `clamp(8px, {core.spacing.2}, 12px)`).
1039
1039
  */
1040
1040
  separation: {
1041
1041
  interactive: {
@@ -1368,6 +1368,47 @@ interface ThemeTokens {
1368
1368
  interface ModeOverride {
1369
1369
  semantic: DeepPartial<ThemeTokens['semantic']>;
1370
1370
  }
1371
+ /**
1372
+ * Machine-readable theme brief — the design intent a theme declares *before*
1373
+ * token values are chosen (theme-authoring.md §"Theme brief"). It is the home
1374
+ * for the `FSL-DESIGN-001..003` gate (posture, density, accessibility target)
1375
+ * so those rules can be validated rather than living only as prose.
1376
+ *
1377
+ * **Orthogonal to tokens.** The brief lives on `ThemeBundle` alongside `base`,
1378
+ * never inside the token tree — DTCG and CSS emitters read `bundle.base`, so a
1379
+ * brief never affects interchange or CSS output. Optional by design: existing
1380
+ * `createTheme` callers stay valid.
1381
+ *
1382
+ * Allowed values mirror theme-authoring.md §"Allowed values".
1383
+ */
1384
+ interface ThemeBrief {
1385
+ /** Theme identity name (e.g. `'base'`, `'bruttal'`). */
1386
+ name: string;
1387
+ /** One line on what experience this theme should produce. */
1388
+ purpose?: string;
1389
+ /** Dominant behavioral personality (FSL-DESIGN-001). */
1390
+ primaryPosture: 'calm' | 'productive' | 'technical' | 'expressive' | 'editorial' | 'premium';
1391
+ /** Optional secondary posture; one posture must dominate. */
1392
+ secondaryPosture?: ThemeBrief['primaryPosture'];
1393
+ /** Operational compactness (FSL-DESIGN-002). */
1394
+ densityProfile: 'compact' | 'balanced' | 'comfortable' | 'spacious';
1395
+ /** Reading vs operating vs scanning bias of the interface. */
1396
+ readingMode?: 'reading' | 'operating' | 'scanning' | 'mixed';
1397
+ /** Primary pointer type the hit-target model targets. */
1398
+ pointerProfile?: 'fine' | 'coarse' | 'hybrid';
1399
+ /** Risk profile of adjacent interactions. */
1400
+ interactionRisk?: 'low' | 'medium' | 'high';
1401
+ /** Surface layering strategy. */
1402
+ surfaceModel?: 'flat' | 'lightly-layered' | 'layered' | 'immersive';
1403
+ /** How much brand presence the theme carries. */
1404
+ brandEnergy?: 'quiet' | 'balanced' | 'expressive';
1405
+ /** Non-negotiable accessibility floor (FSL-DESIGN-003). */
1406
+ accessibilityTarget: 'AA' | 'AA+' | 'AAA-like';
1407
+ /** Color-mode support strategy. */
1408
+ colorModeStrategy?: 'light-only' | 'dark-supported' | 'dark-first' | 'adaptive';
1409
+ /** Primary platform the theme is tuned for. */
1410
+ platformBias?: 'web' | 'mobile' | 'desktop' | 'cross-platform';
1411
+ }
1371
1412
  /**
1372
1413
  * A theme bundle packages a complete `ThemeTokens` (the base)
1373
1414
  * with an optional semantic-only override for the alternate color mode.
@@ -1413,6 +1454,13 @@ interface ThemeBundle {
1413
1454
  * Only semantic references that differ need to be listed — core tokens are shared.
1414
1455
  */
1415
1456
  alternate?: ModeOverride;
1457
+ /**
1458
+ * Machine-readable design brief (posture, density, accessibility target, …).
1459
+ * Orthogonal to tokens — never serialized into DTCG/CSS. Optional so existing
1460
+ * callers stay valid; carrying one lets `FSL-DESIGN-001..003` be enforced.
1461
+ * @see {@link ThemeBrief}
1462
+ */
1463
+ meta?: ThemeBrief;
1416
1464
  }
1417
1465
  /**
1418
1466
  * The semantic token layer of a theme. This is the **only** part of the token
@@ -1424,4 +1472,4 @@ interface ThemeBundle {
1424
1472
  */
1425
1473
  type SemanticTokens = ThemeTokens['semantic'];
1426
1474
  //#endregion
1427
- export { DeepPartial as a, ThemeTokens as i, SemanticTokens as n, CoreDataviz as o, ThemeBundle as r, SemanticDataviz as s, ModeOverride as t };
1475
+ export { ThemeTokens as a, SemanticDataviz as c, ThemeBundle as i, SemanticTokens as n, DeepPartial as o, ThemeBrief as r, CoreDataviz as s, ModeOverride as t };
@@ -42,7 +42,7 @@ interface CoreDataviz {
42
42
  * dataviz.color.scale.diverging.neg3..pos3
43
43
  * dataviz.color.reference.baseline / target
44
44
  * dataviz.color.state.highlight / muted / selected
45
- * dataviz.color.status.missing / suppressed / not-applicable
45
+ * dataviz.color.status.missing / suppressed / notApplicable
46
46
  */
47
47
  interface SemanticDatavizColor {
48
48
  /**
@@ -82,7 +82,7 @@ interface SemanticDatavizColor {
82
82
  status: {
83
83
  /** Value is absent or unavailable. */missing: TokenRef; /** Value is withheld for confidentiality or publication rules. */
84
84
  suppressed: TokenRef; /** Value is structurally absent (not measured or not applicable). */
85
- na: TokenRef;
85
+ notApplicable: TokenRef;
86
86
  };
87
87
  }
88
88
  /**
@@ -440,7 +440,7 @@ interface FeedbackColorStates {
440
440
  interface ColorDimensionOf<S extends BaseColorStates> {
441
441
  /** Fills and surface backgrounds. Use for any colored area larger than a line. */
442
442
  background?: S;
443
- /** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.borders.*` instead — this dimension is colour only. */
443
+ /** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.border.*` instead — this dimension is colour only. */
444
444
  border?: S;
445
445
  /** Readable foreground — labels, paragraphs, and text-like icons that inherit `currentColor`. */
446
446
  text?: S;
@@ -587,7 +587,7 @@ interface SemanticElevation {
587
587
  * Tonal overlay tokens — optional surface color treatments paired with shadows
588
588
  * to preserve depth perception in dark or heavily-colored themes.
589
589
  *
590
- * Each token typically resolves to a color overlay (e.g., `color-mix`, rgba surface).
590
+ * Each token resolves to a color overlay (e.g., `color-mix`, rgba surface).
591
591
  * Omit when the product does not use tonal elevation.
592
592
  * When present, must cover the same strata that carry visible shadows.
593
593
  * @see elevation.md — "Surface + Shadow"
@@ -595,8 +595,8 @@ interface SemanticElevation {
595
595
  tonal?: {
596
596
  /**
597
597
  * Tonal surface treatment paired with `surface.raised`.
598
- * Use when the raised stratum needs an additional color overlay (typical in
599
- * dark themes where shadow alone is insufficient).
598
+ * Use when the raised stratum needs an additional color overlay dark
599
+ * themes where shadow alone is insufficient.
600
600
  * Pair with `surface.raised`; do not use without the matching shadow contract.
601
601
  */
602
602
  raised: CoreColorRef;
@@ -957,13 +957,13 @@ interface GapStackSteps {
957
957
  interface GapInlineSteps {
958
958
  /** Visual-only tight grouping (icon + label inside a single target). Never between focusable targets — use `separation.interactive.min`. */
959
959
  xs: CoreSpacingRef;
960
- /** Inline grouping same magnitude as `gap.stack.xs`. */
960
+ /** Tight inline grouping between related inline siblings. */
961
961
  sm: CoreSpacingRef;
962
- /** Looser inline grouping — same magnitude as `gap.stack.sm`. */
962
+ /** Default inline grouping — pick this when no other step applies. */
963
963
  md: CoreSpacingRef;
964
- /** Spacious inline grouping — same magnitude as `gap.stack.md`. */
964
+ /** Spacious inline grouping. */
965
965
  lg: CoreSpacingRef;
966
- /** Wide inline grouping — same magnitude as `gap.stack.lg`. */
966
+ /** Wide inline grouping — the loosest step before a group break. */
967
967
  xl: CoreSpacingRef;
968
968
  }
969
969
  interface SemanticSpacing {
@@ -1014,7 +1014,7 @@ interface SemanticSpacing {
1014
1014
  /**
1015
1015
  * Structural outer padding for page-level and section-level layout regions.
1016
1016
  * `page` and `section` may use a `clamp()` expression with embedded `{token.path}` refs
1017
- * (e.g. `clamp({core.space.4}, {core.space.6}, {core.space.12})`).
1017
+ * (e.g. `clamp({core.spacing.4}, {core.spacing.6}, {core.spacing.12})`).
1018
1018
  * Typed as `RawValue` to allow both simple refs and responsive clamp expressions.
1019
1019
  */
1020
1020
  gutter: {
@@ -1035,7 +1035,7 @@ interface SemanticSpacing {
1035
1035
  /**
1036
1036
  * Ergonomic separation between independently actionable targets in dense clusters.
1037
1037
  * May use a `clamp()` expression with an embedded `{token.path}` ref
1038
- * (e.g. `clamp(8px, {core.space.2}, 12px)`).
1038
+ * (e.g. `clamp(8px, {core.spacing.2}, 12px)`).
1039
1039
  */
1040
1040
  separation: {
1041
1041
  interactive: {
@@ -1368,6 +1368,47 @@ interface ThemeTokens {
1368
1368
  interface ModeOverride {
1369
1369
  semantic: DeepPartial<ThemeTokens['semantic']>;
1370
1370
  }
1371
+ /**
1372
+ * Machine-readable theme brief — the design intent a theme declares *before*
1373
+ * token values are chosen (theme-authoring.md §"Theme brief"). It is the home
1374
+ * for the `FSL-DESIGN-001..003` gate (posture, density, accessibility target)
1375
+ * so those rules can be validated rather than living only as prose.
1376
+ *
1377
+ * **Orthogonal to tokens.** The brief lives on `ThemeBundle` alongside `base`,
1378
+ * never inside the token tree — DTCG and CSS emitters read `bundle.base`, so a
1379
+ * brief never affects interchange or CSS output. Optional by design: existing
1380
+ * `createTheme` callers stay valid.
1381
+ *
1382
+ * Allowed values mirror theme-authoring.md §"Allowed values".
1383
+ */
1384
+ interface ThemeBrief {
1385
+ /** Theme identity name (e.g. `'base'`, `'bruttal'`). */
1386
+ name: string;
1387
+ /** One line on what experience this theme should produce. */
1388
+ purpose?: string;
1389
+ /** Dominant behavioral personality (FSL-DESIGN-001). */
1390
+ primaryPosture: 'calm' | 'productive' | 'technical' | 'expressive' | 'editorial' | 'premium';
1391
+ /** Optional secondary posture; one posture must dominate. */
1392
+ secondaryPosture?: ThemeBrief['primaryPosture'];
1393
+ /** Operational compactness (FSL-DESIGN-002). */
1394
+ densityProfile: 'compact' | 'balanced' | 'comfortable' | 'spacious';
1395
+ /** Reading vs operating vs scanning bias of the interface. */
1396
+ readingMode?: 'reading' | 'operating' | 'scanning' | 'mixed';
1397
+ /** Primary pointer type the hit-target model targets. */
1398
+ pointerProfile?: 'fine' | 'coarse' | 'hybrid';
1399
+ /** Risk profile of adjacent interactions. */
1400
+ interactionRisk?: 'low' | 'medium' | 'high';
1401
+ /** Surface layering strategy. */
1402
+ surfaceModel?: 'flat' | 'lightly-layered' | 'layered' | 'immersive';
1403
+ /** How much brand presence the theme carries. */
1404
+ brandEnergy?: 'quiet' | 'balanced' | 'expressive';
1405
+ /** Non-negotiable accessibility floor (FSL-DESIGN-003). */
1406
+ accessibilityTarget: 'AA' | 'AA+' | 'AAA-like';
1407
+ /** Color-mode support strategy. */
1408
+ colorModeStrategy?: 'light-only' | 'dark-supported' | 'dark-first' | 'adaptive';
1409
+ /** Primary platform the theme is tuned for. */
1410
+ platformBias?: 'web' | 'mobile' | 'desktop' | 'cross-platform';
1411
+ }
1371
1412
  /**
1372
1413
  * A theme bundle packages a complete `ThemeTokens` (the base)
1373
1414
  * with an optional semantic-only override for the alternate color mode.
@@ -1413,6 +1454,13 @@ interface ThemeBundle {
1413
1454
  * Only semantic references that differ need to be listed — core tokens are shared.
1414
1455
  */
1415
1456
  alternate?: ModeOverride;
1457
+ /**
1458
+ * Machine-readable design brief (posture, density, accessibility target, …).
1459
+ * Orthogonal to tokens — never serialized into DTCG/CSS. Optional so existing
1460
+ * callers stay valid; carrying one lets `FSL-DESIGN-001..003` be enforced.
1461
+ * @see {@link ThemeBrief}
1462
+ */
1463
+ meta?: ThemeBrief;
1416
1464
  }
1417
1465
  /**
1418
1466
  * The semantic token layer of a theme. This is the **only** part of the token
@@ -1424,4 +1472,4 @@ interface ThemeBundle {
1424
1472
  */
1425
1473
  type SemanticTokens = ThemeTokens['semantic'];
1426
1474
  //#endregion
1427
- export { DeepPartial as a, ThemeTokens as i, SemanticTokens as n, CoreDataviz as o, ThemeBundle as r, SemanticDataviz as s, ModeOverride as t };
1475
+ export { ThemeTokens as a, SemanticDataviz as c, ThemeBundle as i, SemanticTokens as n, DeepPartial as o, ThemeBrief as r, CoreDataviz as s, ModeOverride as t };
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- const require_createTheme = require('./createTheme-BHGc1IFI.cjs');
2
+ const require_createTheme = require('./createTheme--UFgVlEn.cjs');
3
3
 
4
4
  //#region src/baseBundle.ts
5
5
  /**
@@ -10,7 +10,8 @@ const require_createTheme = require('./createTheme-BHGc1IFI.cjs');
10
10
  */
11
11
  var baseBundle = require_createTheme.createTheme({
12
12
  base: require_createTheme.baseTheme,
13
- alternate: require_createTheme.darkAlternate
13
+ alternate: require_createTheme.darkAlternate,
14
+ brief: require_createTheme.baseBrief
14
15
  });
15
16
 
16
17
  //#endregion
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { i as darkAlternate, n as createTheme, r as baseTheme } from "./createTheme-C1r5nJco.mjs";
2
+ import { a as darkAlternate, i as baseTheme, n as createTheme, r as baseBrief } from "./createTheme-B6tVIikC.mjs";
3
3
 
4
4
  //#region src/baseBundle.ts
5
5
  /**
@@ -10,7 +10,8 @@ import { i as darkAlternate, n as createTheme, r as baseTheme } from "./createTh
10
10
  */
11
11
  var baseBundle = createTheme({
12
12
  base: baseTheme,
13
- alternate: darkAlternate
13
+ alternate: darkAlternate,
14
+ brief: baseBrief
14
15
  });
15
16
 
16
17
  //#endregion
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- const require_helpers = require('./helpers-CIqeJ8yd.cjs');
2
+ const require_helpers = require('./helpers-BU40JvFA.cjs');
3
3
 
4
4
  //#region src/baseTheme.ts
5
5
  /**
@@ -1741,6 +1741,26 @@ var darkAlternate = {
1741
1741
  }
1742
1742
  }
1743
1743
  };
1744
+ /**
1745
+ * Design brief for the base theme — the recommended default from
1746
+ * theme-authoring.md §"Recommended base theme brief". Attached to the base
1747
+ * bundle via `createTheme({ brief })`; carries the `FSL-DESIGN-001..003` gate.
1748
+ */
1749
+ var baseBrief = {
1750
+ name: "base",
1751
+ purpose: "default built-in foundation for modern product UI",
1752
+ primaryPosture: "productive",
1753
+ secondaryPosture: "calm",
1754
+ densityProfile: "balanced",
1755
+ readingMode: "mixed",
1756
+ pointerProfile: "hybrid",
1757
+ interactionRisk: "medium",
1758
+ surfaceModel: "lightly-layered",
1759
+ brandEnergy: "quiet",
1760
+ accessibilityTarget: "AA+",
1761
+ colorModeStrategy: "dark-supported",
1762
+ platformBias: "web"
1763
+ };
1744
1764
 
1745
1765
  //#endregion
1746
1766
  //#region src/roots/validateRefs.ts
@@ -1880,22 +1900,33 @@ var createTheme = ({
1880
1900
  baseMode,
1881
1901
  base,
1882
1902
  overrides,
1883
- alternate
1903
+ alternate,
1904
+ brief
1884
1905
  } = {}) => {
1885
1906
  const resolvedBase = base ?? parentBundle?.base;
1886
1907
  const resolvedBaseMode = baseMode ?? parentBundle?.baseMode ?? "light";
1887
1908
  const resolvedAlternate = resolveAlternate(alternate, parentBundle);
1909
+ const resolvedMeta = brief ?? parentBundle?.meta;
1888
1910
  return {
1889
1911
  baseMode: resolvedBaseMode,
1890
1912
  base: buildTheme({
1891
1913
  base: resolvedBase,
1892
1914
  overrides
1893
1915
  }),
1894
- alternate: resolvedAlternate
1916
+ alternate: resolvedAlternate,
1917
+ ...(resolvedMeta ? {
1918
+ meta: resolvedMeta
1919
+ } : {})
1895
1920
  };
1896
1921
  };
1897
1922
 
1898
1923
  //#endregion
1924
+ Object.defineProperty(exports, 'baseBrief', {
1925
+ enumerable: true,
1926
+ get: function () {
1927
+ return baseBrief;
1928
+ }
1929
+ });
1899
1930
  Object.defineProperty(exports, 'baseTheme', {
1900
1931
  enumerable: true,
1901
1932
  get: function () {
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { a as flattenTheme, r as deepMerge, t as COMPOUND_REF_RE } from "./helpers-Dz7NfNK6.mjs";
2
+ import { a as flattenTheme, r as deepMerge, t as COMPOUND_REF_RE } from "./helpers-BXaKe2s3.mjs";
3
3
 
4
4
  //#region src/baseTheme.ts
5
5
  /**
@@ -1741,6 +1741,26 @@ var darkAlternate = {
1741
1741
  }
1742
1742
  }
1743
1743
  };
1744
+ /**
1745
+ * Design brief for the base theme — the recommended default from
1746
+ * theme-authoring.md §"Recommended base theme brief". Attached to the base
1747
+ * bundle via `createTheme({ brief })`; carries the `FSL-DESIGN-001..003` gate.
1748
+ */
1749
+ var baseBrief = {
1750
+ name: "base",
1751
+ purpose: "default built-in foundation for modern product UI",
1752
+ primaryPosture: "productive",
1753
+ secondaryPosture: "calm",
1754
+ densityProfile: "balanced",
1755
+ readingMode: "mixed",
1756
+ pointerProfile: "hybrid",
1757
+ interactionRisk: "medium",
1758
+ surfaceModel: "lightly-layered",
1759
+ brandEnergy: "quiet",
1760
+ accessibilityTarget: "AA+",
1761
+ colorModeStrategy: "dark-supported",
1762
+ platformBias: "web"
1763
+ };
1744
1764
 
1745
1765
  //#endregion
1746
1766
  //#region src/roots/validateRefs.ts
@@ -1880,20 +1900,25 @@ var createTheme = ({
1880
1900
  baseMode,
1881
1901
  base,
1882
1902
  overrides,
1883
- alternate
1903
+ alternate,
1904
+ brief
1884
1905
  } = {}) => {
1885
1906
  const resolvedBase = base ?? parentBundle?.base;
1886
1907
  const resolvedBaseMode = baseMode ?? parentBundle?.baseMode ?? "light";
1887
1908
  const resolvedAlternate = resolveAlternate(alternate, parentBundle);
1909
+ const resolvedMeta = brief ?? parentBundle?.meta;
1888
1910
  return {
1889
1911
  baseMode: resolvedBaseMode,
1890
1912
  base: buildTheme({
1891
1913
  base: resolvedBase,
1892
1914
  overrides
1893
1915
  }),
1894
- alternate: resolvedAlternate
1916
+ alternate: resolvedAlternate,
1917
+ ...(resolvedMeta ? {
1918
+ meta: resolvedMeta
1919
+ } : {})
1895
1920
  };
1896
1921
  };
1897
1922
 
1898
1923
  //#endregion
1899
- export { darkAlternate as i, createTheme as n, baseTheme as r, buildTheme as t };
1924
+ export { darkAlternate as a, baseTheme as i, createTheme as n, baseBrief as r, buildTheme as t };
package/dist/css.cjs CHANGED
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, {
3
3
  value: 'Module'
4
4
  });
5
- const require_helpers = require('./helpers-CIqeJ8yd.cjs');
6
- const require_toCssVars = require('./toCssVars-DDowz4wU.cjs');
5
+ const require_helpers = require('./helpers-BU40JvFA.cjs');
6
+ const require_toCssVars = require('./toCssVars-BseqmMcT.cjs');
7
7
 
8
8
  //#region src/css.ts
9
9
  /**
package/dist/css.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as ThemeTokens, r as ThemeBundle } from "./Types-BiBa17RL.cjs";
2
+ import { a as ThemeTokens, i as ThemeBundle } from "./Types-BkaxtsMv.cjs";
3
3
 
4
4
  //#region src/roots/helpers.d.ts
5
5
  /**
package/dist/css.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as ThemeTokens, r as ThemeBundle } from "./Types-BiBa17RL.mjs";
2
+ import { a as ThemeTokens, i as ThemeBundle } from "./Types-BkaxtsMv.mjs";
3
3
 
4
4
  //#region src/roots/helpers.d.ts
5
5
  /**
package/dist/css.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { s as toFlatTokens } from "./helpers-Dz7NfNK6.mjs";
3
- import { n as toCssVars, t as toCssVarName } from "./toCssVars-CeckLjW1.mjs";
2
+ import { s as toFlatTokens } from "./helpers-BXaKe2s3.mjs";
3
+ import { n as toCssVars, t as toCssVarName } from "./toCssVars-C-9yIZDR.mjs";
4
4
 
5
5
  //#region src/css.ts
6
6
  /**
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, {
3
3
  value: 'Module'
4
4
  });
5
- const require_react = require('../react-BKLEzIN8.cjs');
6
- const require_withDataviz = require('../withDataviz-BzZwFV2e.cjs');
5
+ const require_react = require('../react-DLB8nrXQ.cjs');
6
+ const require_withDataviz = require('../withDataviz-CYRf4exT.cjs');
7
7
 
8
8
  //#region src/dataviz/useDatavizTokens.ts
9
9
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { o as CoreDataviz, r as ThemeBundle, s as SemanticDataviz } from "../Types-BiBa17RL.cjs";
2
+ import { c as SemanticDataviz, i as ThemeBundle, s as CoreDataviz } from "../Types-BkaxtsMv.cjs";
3
3
 
4
4
  //#region src/dataviz/baseTheme.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { o as CoreDataviz, r as ThemeBundle, s as SemanticDataviz } from "../Types-BiBa17RL.mjs";
2
+ import { c as SemanticDataviz, i as ThemeBundle, s as CoreDataviz } from "../Types-BkaxtsMv.mjs";
3
3
 
4
4
  //#region src/dataviz/baseTheme.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { n as coreDataviz, r as semanticDataviz, t as withDataviz } from "../withDataviz-CrgXLR5A.mjs";
2
+ import { n as coreDataviz, r as semanticDataviz, t as withDataviz } from "../withDataviz-BvpZJnFx.mjs";
3
3
  import { useTokens } from "../react.mjs";
4
4
 
5
5
  //#region src/dataviz/useDatavizTokens.ts