@ttoss/fsl-theme 1.1.21 → 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.
- package/README.md +16 -14
- package/dist/{Types-BiBa17RL.d.cts → Types-BkaxtsMv.d.cts} +61 -13
- package/dist/{Types-BiBa17RL.d.mts → Types-BkaxtsMv.d.mts} +61 -13
- package/dist/{baseBundle-BXvPJYzj.cjs → baseBundle-DdRSa5mf.cjs} +3 -2
- package/dist/{baseBundle-BLvD8XVS.mjs → baseBundle-R7WyjyTw.mjs} +3 -2
- package/dist/{createTheme-JOu16Shb.cjs → createTheme--UFgVlEn.cjs} +144 -16
- package/dist/{createTheme-DzLnsknT.mjs → createTheme-B6tVIikC.mjs} +138 -16
- package/dist/css.cjs +2 -2
- package/dist/css.d.cts +1 -1
- package/dist/css.d.mts +1 -1
- package/dist/css.mjs +2 -2
- package/dist/dataviz/index.cjs +2 -2
- package/dist/dataviz/index.d.cts +1 -1
- package/dist/dataviz/index.d.mts +1 -1
- package/dist/dataviz/index.mjs +1 -1
- package/dist/dtcg.cjs +26 -9
- package/dist/dtcg.d.cts +17 -4
- package/dist/dtcg.d.mts +17 -4
- package/dist/dtcg.mjs +26 -9
- package/dist/{helpers-C29fy-Ad.cjs → helpers-BU40JvFA.cjs} +27 -78
- package/dist/{helpers-CA4Qkxct.mjs → helpers-BXaKe2s3.mjs} +28 -73
- package/dist/{index-CsIjfw86.d.cts → index-7YL90Xqs.d.cts} +36 -12
- package/dist/{index-nJrjI0BA.d.mts → index-BmCTIOzh.d.mts} +36 -12
- package/dist/index.cjs +2 -8
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -5
- package/dist/{react-DnKxR2gK.d.mts → react-C8JrZf-b.d.mts} +43 -2
- package/dist/{react-CoWIQYx_.cjs → react-DLB8nrXQ.cjs} +32 -2
- package/dist/{react-CGa6FlNL.d.cts → react-Dt7vGAVG.d.cts} +43 -2
- package/dist/react.cjs +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/react.mjs +32 -2
- package/dist/runtime-entry.cjs +1 -1
- package/dist/runtime-entry.mjs +1 -1
- package/dist/{ssrScript-CoRE6jjq.mjs → ssrScript-DNLlLDps.mjs} +2 -1
- package/dist/{ssrScript-DOpLExaG.cjs → ssrScript-MF5SIONH.cjs} +2 -1
- package/dist/themes/bruttal.cjs +19 -4
- package/dist/themes/bruttal.d.cts +1 -1
- package/dist/themes/bruttal.d.mts +1 -1
- package/dist/themes/bruttal.mjs +19 -4
- package/dist/{toCssVars-CUiqx2kh.cjs → toCssVars-BseqmMcT.cjs} +27 -2
- package/dist/{toCssVars-BHRiSykR.mjs → toCssVars-C-9yIZDR.mjs} +27 -2
- package/dist/{tokenRegistry-DolhB5Vf.mjs → tokenRegistry-DviyqDeE.mjs} +19 -28
- package/dist/{tokenRegistry-B9zMRjpL.cjs → tokenRegistry-f_Bx_BAN.cjs} +19 -28
- package/dist/vars.cjs +3 -3
- package/dist/vars.d.cts +1 -1
- package/dist/vars.d.mts +1 -1
- package/dist/vars.mjs +3 -3
- package/dist/{withDataviz-DYV1S_xr.mjs → withDataviz-BvpZJnFx.mjs} +2 -2
- package/dist/{withDataviz-BlJcYhcw.cjs → withDataviz-CYRf4exT.cjs} +2 -2
- package/llms.txt +6 -6
- package/package.json +1 -1
- package/dist/themes/corporate.cjs +0 -34
- package/dist/themes/corporate.d.cts +0 -3
- package/dist/themes/corporate.d.mts +0 -3
- package/dist/themes/corporate.mjs +0 -31
- package/dist/themes/oca.cjs +0 -34
- package/dist/themes/oca.d.cts +0 -3
- package/dist/themes/oca.d.mts +0 -3
- package/dist/themes/oca.mjs +0 -31
- package/dist/themes/ventures.cjs +0 -34
- package/dist/themes/ventures.d.cts +0 -3
- package/dist/themes/ventures.d.mts +0 -3
- 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
|
|
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.{
|
|
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}` |
|
|
@@ -164,9 +164,9 @@ Find the CSS property you are setting, then follow the branch to the leaf.
|
|
|
164
164
|
|
|
165
165
|
## Per-family specs
|
|
166
166
|
|
|
167
|
-
Full grammar rules, decision matrices, and anti-patterns live in the family specs. Each file follows the pattern `
|
|
167
|
+
Full grammar rules, decision matrices, and anti-patterns live in the family specs. Each file follows the pattern `families/{family}.md`:
|
|
168
168
|
|
|
169
|
-
[colors](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/
|
|
169
|
+
[colors](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/colors.md) · [spacing](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/spacing.md) · [typography](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/typography.md) · [sizing](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/sizing.md) · [radii](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/radii.md) · [borders](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/borders.md) · [elevation](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/elevation.md) · [opacity](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/opacity.md) · [motion](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/motion.md) · [z-index](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/z-index.md) · [breakpoints](https://github.com/ttoss/ttoss/blob/main/docs/website/docs/design/design-system/design-tokens/families/breakpoints.md)
|
|
170
170
|
|
|
171
171
|
## createTheme
|
|
172
172
|
|
|
@@ -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
|
|
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 /
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
599
|
-
*
|
|
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
|
-
/**
|
|
960
|
+
/** Tight inline grouping between related inline siblings. */
|
|
961
961
|
sm: CoreSpacingRef;
|
|
962
|
-
/**
|
|
962
|
+
/** Default inline grouping — pick this when no other step applies. */
|
|
963
963
|
md: CoreSpacingRef;
|
|
964
|
-
/** Spacious inline grouping
|
|
964
|
+
/** Spacious inline grouping. */
|
|
965
965
|
lg: CoreSpacingRef;
|
|
966
|
-
/** Wide inline grouping —
|
|
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.
|
|
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.
|
|
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 {
|
|
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 /
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
599
|
-
*
|
|
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
|
-
/**
|
|
960
|
+
/** Tight inline grouping between related inline siblings. */
|
|
961
961
|
sm: CoreSpacingRef;
|
|
962
|
-
/**
|
|
962
|
+
/** Default inline grouping — pick this when no other step applies. */
|
|
963
963
|
md: CoreSpacingRef;
|
|
964
|
-
/** Spacious inline grouping
|
|
964
|
+
/** Spacious inline grouping. */
|
|
965
965
|
lg: CoreSpacingRef;
|
|
966
|
-
/** Wide inline grouping —
|
|
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.
|
|
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.
|
|
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 {
|
|
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
|
|
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-JOu16Shb.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 {
|
|
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-
|
|
2
|
+
const require_helpers = require('./helpers-BU40JvFA.cjs');
|
|
3
3
|
|
|
4
4
|
//#region src/baseTheme.ts
|
|
5
5
|
/**
|
|
@@ -7,6 +7,33 @@ const require_helpers = require('./helpers-C29fy-Ad.cjs');
|
|
|
7
7
|
*
|
|
8
8
|
* System fonts, gray palette, and balanced proportions. Serves as the
|
|
9
9
|
* canonical base that all other themes extend via `createTheme`.
|
|
10
|
+
*
|
|
11
|
+
* ## Theme brief
|
|
12
|
+
*
|
|
13
|
+
* Every value in this file is chosen so that the resolved bundle satisfies
|
|
14
|
+
* this brief. Changes that contradict the brief belong in a different theme,
|
|
15
|
+
* not in `baseTheme`.
|
|
16
|
+
*
|
|
17
|
+
* ```yaml
|
|
18
|
+
* name: base
|
|
19
|
+
* purpose: default built-in foundation for modern product UI
|
|
20
|
+
* primaryPosture: productive
|
|
21
|
+
* secondaryPosture: calm
|
|
22
|
+
* densityProfile: balanced
|
|
23
|
+
* readingMode: mixed
|
|
24
|
+
* pointerProfile: hybrid
|
|
25
|
+
* interactionRisk: medium
|
|
26
|
+
* surfaceModel: lightly-layered
|
|
27
|
+
* brandEnergy: quiet
|
|
28
|
+
* accessibilityTarget: AA+
|
|
29
|
+
* colorModeStrategy: dark-supported
|
|
30
|
+
* platformBias: web
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Intended feel: practical, calm, modern, trustworthy, easy to extend.
|
|
34
|
+
* Avoided feel: flashy, ornamental, cramped, fragile, overly branded.
|
|
35
|
+
*
|
|
36
|
+
* @see /docs/website/docs/design/design-system/design-tokens/theme-authoring.md#recommended-base-theme-brief
|
|
10
37
|
*/
|
|
11
38
|
var baseTheme = {
|
|
12
39
|
core: {
|
|
@@ -1077,23 +1104,23 @@ var baseTheme = {
|
|
|
1077
1104
|
spacing: {
|
|
1078
1105
|
inset: {
|
|
1079
1106
|
control: {
|
|
1080
|
-
sm: "{core.spacing.2}",
|
|
1081
|
-
md: "{core.spacing.3}",
|
|
1082
|
-
lg: "{core.spacing.4}"
|
|
1083
|
-
},
|
|
1084
|
-
surface: {
|
|
1085
1107
|
sm: "{core.spacing.3}",
|
|
1086
1108
|
md: "{core.spacing.4}",
|
|
1087
1109
|
lg: "{core.spacing.6}"
|
|
1110
|
+
},
|
|
1111
|
+
surface: {
|
|
1112
|
+
sm: "{core.spacing.4}",
|
|
1113
|
+
md: "{core.spacing.6}",
|
|
1114
|
+
lg: "{core.spacing.8}"
|
|
1088
1115
|
}
|
|
1089
1116
|
},
|
|
1090
1117
|
gap: {
|
|
1091
1118
|
stack: {
|
|
1092
1119
|
xs: "{core.spacing.2}",
|
|
1093
|
-
sm: "{core.spacing.
|
|
1094
|
-
md: "{core.spacing.
|
|
1095
|
-
lg: "{core.spacing.
|
|
1096
|
-
xl: "{core.spacing.
|
|
1120
|
+
sm: "{core.spacing.4}",
|
|
1121
|
+
md: "{core.spacing.6}",
|
|
1122
|
+
lg: "{core.spacing.8}",
|
|
1123
|
+
xl: "{core.spacing.12}"
|
|
1097
1124
|
},
|
|
1098
1125
|
inline: {
|
|
1099
1126
|
xs: "{core.spacing.1}",
|
|
@@ -1104,12 +1131,12 @@ var baseTheme = {
|
|
|
1104
1131
|
}
|
|
1105
1132
|
},
|
|
1106
1133
|
gutter: {
|
|
1107
|
-
page: "clamp({core.spacing.
|
|
1108
|
-
section: "clamp({core.spacing.
|
|
1134
|
+
page: "clamp({core.spacing.6}, {core.spacing.12}, {core.spacing.16})",
|
|
1135
|
+
section: "clamp({core.spacing.4}, {core.spacing.8}, {core.spacing.16})"
|
|
1109
1136
|
},
|
|
1110
1137
|
separation: {
|
|
1111
1138
|
interactive: {
|
|
1112
|
-
min: "clamp(8px, {core.spacing.
|
|
1139
|
+
min: "clamp(8px, {core.spacing.3}, 16px)"
|
|
1113
1140
|
}
|
|
1114
1141
|
}
|
|
1115
1142
|
},
|
|
@@ -1714,6 +1741,96 @@ var darkAlternate = {
|
|
|
1714
1741
|
}
|
|
1715
1742
|
}
|
|
1716
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
|
+
};
|
|
1764
|
+
|
|
1765
|
+
//#endregion
|
|
1766
|
+
//#region src/roots/validateRefs.ts
|
|
1767
|
+
/**
|
|
1768
|
+
* Compute the Levenshtein edit distance between two strings.
|
|
1769
|
+
* Used exclusively by `validateRefs` to power "did you mean?" suggestions.
|
|
1770
|
+
*/
|
|
1771
|
+
var levenshtein = (a, b) => {
|
|
1772
|
+
const m = a.length;
|
|
1773
|
+
const n = b.length;
|
|
1774
|
+
const dp = Array.from({
|
|
1775
|
+
length: m + 1
|
|
1776
|
+
}, () => {
|
|
1777
|
+
return Array.from({
|
|
1778
|
+
length: n + 1
|
|
1779
|
+
}, () => {
|
|
1780
|
+
return 0;
|
|
1781
|
+
});
|
|
1782
|
+
});
|
|
1783
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
1784
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
1785
|
+
for (let i = 1; i <= m; i++) for (let j = 1; j <= n; j++) dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
1786
|
+
return dp[m][n];
|
|
1787
|
+
};
|
|
1788
|
+
/**
|
|
1789
|
+
* Validate that every `{ref}` in the merged theme points to an existing path.
|
|
1790
|
+
* Emits `console.warn` for each broken reference with a "did you mean?" suggestion.
|
|
1791
|
+
*
|
|
1792
|
+
* **DEV-only** — callers gate this behind `process.env.NODE_ENV !== 'production'`
|
|
1793
|
+
* so bundlers tree-shake the entire call in production builds.
|
|
1794
|
+
*/
|
|
1795
|
+
var validateRefs = theme => {
|
|
1796
|
+
const {
|
|
1797
|
+
core,
|
|
1798
|
+
semantic
|
|
1799
|
+
} = require_helpers.flattenTheme(theme);
|
|
1800
|
+
const all = {
|
|
1801
|
+
...core,
|
|
1802
|
+
...semantic
|
|
1803
|
+
};
|
|
1804
|
+
const allKeys = Object.keys(all);
|
|
1805
|
+
const findSuggestion = (refPath, candidates) => {
|
|
1806
|
+
if (candidates.length === 0) return "";
|
|
1807
|
+
let bestDist = Infinity;
|
|
1808
|
+
let bestKey = "";
|
|
1809
|
+
for (const candidate of candidates) {
|
|
1810
|
+
const dist = levenshtein(refPath, candidate);
|
|
1811
|
+
if (dist < bestDist) {
|
|
1812
|
+
bestDist = dist;
|
|
1813
|
+
bestKey = candidate;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
if (bestDist <= Math.ceil(refPath.length * .4)) return `\n Did you mean '{${bestKey}}'?`;
|
|
1817
|
+
return "";
|
|
1818
|
+
};
|
|
1819
|
+
for (const [ownerKey, value] of Object.entries(all)) {
|
|
1820
|
+
if (typeof value !== "string" || !value.includes("{")) continue;
|
|
1821
|
+
let match;
|
|
1822
|
+
const re = new RegExp(require_helpers.COMPOUND_REF_RE.source, require_helpers.COMPOUND_REF_RE.flags);
|
|
1823
|
+
while ((match = re.exec(value)) !== null) {
|
|
1824
|
+
const refPath = match[1];
|
|
1825
|
+
if (all[refPath] !== void 0) continue;
|
|
1826
|
+
const prefix = refPath.split(".")[0];
|
|
1827
|
+
const suggestion = findSuggestion(refPath, allKeys.filter(k => {
|
|
1828
|
+
return k.startsWith(prefix + ".");
|
|
1829
|
+
}));
|
|
1830
|
+
console.warn(`[fsl-theme] Invalid token reference '{${refPath}}' at path '${ownerKey}'.${suggestion}`);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1717
1834
|
|
|
1718
1835
|
//#endregion
|
|
1719
1836
|
//#region src/createTheme.ts
|
|
@@ -1728,7 +1845,7 @@ var buildTheme = ({
|
|
|
1728
1845
|
overrides = {}
|
|
1729
1846
|
} = {}) => {
|
|
1730
1847
|
const merged = require_helpers.deepMerge(base, overrides);
|
|
1731
|
-
if (process.env.NODE_ENV !== "production")
|
|
1848
|
+
if (process.env.NODE_ENV !== "production") validateRefs(merged);
|
|
1732
1849
|
return structuredClone(merged);
|
|
1733
1850
|
};
|
|
1734
1851
|
var resolveAlternate = (alternate, parentBundle) => {
|
|
@@ -1783,22 +1900,33 @@ var createTheme = ({
|
|
|
1783
1900
|
baseMode,
|
|
1784
1901
|
base,
|
|
1785
1902
|
overrides,
|
|
1786
|
-
alternate
|
|
1903
|
+
alternate,
|
|
1904
|
+
brief
|
|
1787
1905
|
} = {}) => {
|
|
1788
1906
|
const resolvedBase = base ?? parentBundle?.base;
|
|
1789
1907
|
const resolvedBaseMode = baseMode ?? parentBundle?.baseMode ?? "light";
|
|
1790
1908
|
const resolvedAlternate = resolveAlternate(alternate, parentBundle);
|
|
1909
|
+
const resolvedMeta = brief ?? parentBundle?.meta;
|
|
1791
1910
|
return {
|
|
1792
1911
|
baseMode: resolvedBaseMode,
|
|
1793
1912
|
base: buildTheme({
|
|
1794
1913
|
base: resolvedBase,
|
|
1795
1914
|
overrides
|
|
1796
1915
|
}),
|
|
1797
|
-
alternate: resolvedAlternate
|
|
1916
|
+
alternate: resolvedAlternate,
|
|
1917
|
+
...(resolvedMeta ? {
|
|
1918
|
+
meta: resolvedMeta
|
|
1919
|
+
} : {})
|
|
1798
1920
|
};
|
|
1799
1921
|
};
|
|
1800
1922
|
|
|
1801
1923
|
//#endregion
|
|
1924
|
+
Object.defineProperty(exports, 'baseBrief', {
|
|
1925
|
+
enumerable: true,
|
|
1926
|
+
get: function () {
|
|
1927
|
+
return baseBrief;
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1802
1930
|
Object.defineProperty(exports, 'baseTheme', {
|
|
1803
1931
|
enumerable: true,
|
|
1804
1932
|
get: function () {
|