@ttoss/fsl-theme 1.1.22 → 2.1.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 +54 -41
- package/dist/{Types-BiBa17RL.d.cts → Types-a_sfpbha.d.cts} +84 -24
- package/dist/{Types-BiBa17RL.d.mts → Types-a_sfpbha.d.mts} +84 -24
- package/dist/{baseBundle-DiYlHNG5.mjs → baseBundle-CXdxj_re.mjs} +3 -2
- package/dist/{baseBundle-BWbICg4p.cjs → baseBundle-a9LDsqYy.cjs} +3 -2
- package/dist/{createTheme-BHGc1IFI.cjs → createTheme-B7H0xSVp.cjs} +128 -19
- package/dist/{createTheme-C1r5nJco.mjs → createTheme-Bm9LVQ4w.mjs} +123 -20
- package/dist/css.cjs +5 -4
- package/dist/css.d.cts +21 -2
- package/dist/css.d.mts +21 -2
- package/dist/css.mjs +5 -4
- 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-CIqeJ8yd.cjs → helpers-BU40JvFA.cjs} +27 -5
- package/dist/{helpers-Dz7NfNK6.mjs → helpers-BXaKe2s3.mjs} +27 -5
- package/dist/{index-BuszkWMy.d.mts → index-BmZQVd3G.d.cts} +9 -12
- package/dist/{index-Djro6G4e.d.cts → index-XZsRrMTF.d.mts} +9 -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-D2J482kx.d.mts → react-BwsjWKmo.d.mts} +59 -16
- package/dist/{react-BKLEzIN8.cjs → react-DQErMGJz.cjs} +107 -19
- package/dist/{react-BXqSYnmk.d.cts → react-Drr-QSuX.d.cts} +59 -16
- package/dist/react.cjs +3 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/react.mjs +109 -19
- package/dist/runtime-entry.cjs +1 -1
- package/dist/runtime-entry.mjs +1 -1
- package/dist/{ssrScript-D3mYscp1.cjs → ssrScript-D73WcrCM.cjs} +52 -4
- package/dist/{ssrScript-DfmopH-w.mjs → ssrScript-zDK1dqXr.mjs} +52 -4
- 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-CeckLjW1.mjs → toCssVars-CWTlp9St.mjs} +65 -5
- package/dist/{toCssVars-DDowz4wU.cjs → toCssVars-D0BR23cP.cjs} +65 -5
- 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 +8 -8
- package/dist/vars.d.cts +6 -6
- package/dist/vars.d.mts +6 -6
- package/dist/vars.mjs +8 -8
- package/dist/{withDataviz-BzZwFV2e.cjs → withDataviz-DrUtw8cc.cjs} +2 -2
- package/dist/{withDataviz-CrgXLR5A.mjs → withDataviz-Ln3sEwb7.mjs} +2 -2
- package/llms.txt +11 -9
- 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
|
|
|
@@ -34,38 +34,39 @@ Components consume only semantic tokens. Core tokens never change between light
|
|
|
34
34
|
|
|
35
35
|
One entry per semantic family. Use `vars.*` for typed CSS variable references; use the `semantic.*` path shape below when naming tokens in TypeScript or in discussion.
|
|
36
36
|
|
|
37
|
-
| Family | Path shape
|
|
38
|
-
| --------- |
|
|
39
|
-
| colors | `semantic.colors.{ux}.{role}.{dimension}.{state}`
|
|
40
|
-
| spacing | `semantic.spacing.inset.{control,surface}.{sm,md,lg}`
|
|
41
|
-
| | `semantic.spacing.gap.{stack,inline}.{xs,sm,md,lg,xl}`
|
|
42
|
-
| | `semantic.spacing.gutter.{page,section}`
|
|
43
|
-
| | `semantic.spacing.separation.interactive.min`
|
|
44
|
-
| text | `semantic.text.{display,headline,title,body,label
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
| | `semantic.sizing.
|
|
48
|
-
| | `semantic.sizing.
|
|
49
|
-
| | `semantic.sizing.
|
|
50
|
-
| | `semantic.sizing.
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
37
|
+
| Family | Path shape | Leaf value |
|
|
38
|
+
| --------- | ---------------------------------------------------------------- | ----------------------- |
|
|
39
|
+
| colors | `semantic.colors.{ux}.{role}.{dimension}.{state}` | CSS color |
|
|
40
|
+
| spacing | `semantic.spacing.inset.{control,surface}.{sm,md,lg}` | CSS length |
|
|
41
|
+
| | `semantic.spacing.gap.{stack,inline}.{xs,sm,md,lg,xl}` | CSS length |
|
|
42
|
+
| | `semantic.spacing.gutter.{page,section}` | CSS length / `clamp()` |
|
|
43
|
+
| | `semantic.spacing.separation.interactive.min` | CSS length |
|
|
44
|
+
| text | `semantic.text.{display,headline,title,body,label}.{lg,md,sm}` | TextStyle object |
|
|
45
|
+
| | `semantic.text.code.{md,sm}` | TextStyle object |
|
|
46
|
+
| sizing | `semantic.sizing.hit.{min,base,prominent}` | CSS length |
|
|
47
|
+
| | `semantic.sizing.icon.{sm,md,lg}` | CSS length |
|
|
48
|
+
| | `semantic.sizing.identity.{sm,md,lg,xl}` | CSS length |
|
|
49
|
+
| | `semantic.sizing.measure.reading` | CSS `ch` / `clamp()` |
|
|
50
|
+
| | `semantic.sizing.surface.maxWidth` | CSS length |
|
|
51
|
+
| | `semantic.sizing.viewport.{height,width}.full` | CSS dvh/dvw |
|
|
52
|
+
| radii | `semantic.radii.{control,surface,round}` | CSS length |
|
|
53
|
+
| border | `semantic.border.divider` | `{width, style}` |
|
|
54
|
+
| | `semantic.border.outline.{surface,control,selected}` | `{width, style}` |
|
|
55
|
+
| focus | `semantic.focus.ring` | `{width, style, color}` |
|
|
56
|
+
| elevation | `semantic.elevation.surface.{flat,raised,overlay,blocking}` | CSS box-shadow |
|
|
57
|
+
| | `semantic.elevation.tonal.{raised,overlay,blocking}` | CSS color (optional) |
|
|
58
|
+
| opacity | `semantic.opacity.{scrim,loading,disabled}` | number in (0, 1) |
|
|
59
|
+
| overlay | `semantic.overlay.scrim` | CSS color with alpha |
|
|
60
|
+
| motion | `semantic.motion.{feedback,emphasis,decorative}` | `{duration, easing}` |
|
|
61
|
+
| | `semantic.motion.transition.{enter,exit}` | `{duration, easing}` |
|
|
62
|
+
| zIndex | `semantic.zIndex.layer.{base,sticky,overlay,blocking,transient}` | integer |
|
|
62
63
|
|
|
63
64
|
**Colors axes** (`semantic.colors.{ux}.{role}.{dimension}.{state}`):
|
|
64
65
|
|
|
65
66
|
- `ux` — FSL Entity Kind: `action` · `input` · `navigation` · `feedback` · `informational`
|
|
66
67
|
- `role` — Evaluation: `primary` · `secondary` · `accent` · `muted` · `positive` · `caution` · `negative`
|
|
67
68
|
- `dimension` — `background` · `border` · `text`
|
|
68
|
-
- `state` — `default` · `hover` · `active` · `focused` · `disabled` · `selected` · `pressed`
|
|
69
|
+
- `state` — `default` · `hover` · `active` · `focused` · `disabled` · `selected` · `pressed` · `expanded` · `checked` · `indeterminate` · `current` · `visited` · `droptarget` · `invalid` (legality varies per `ux` — see the colors family spec)
|
|
69
70
|
|
|
70
71
|
## Pick a token in 60s
|
|
71
72
|
|
|
@@ -220,7 +221,7 @@ export const App = () => (
|
|
|
220
221
|
);
|
|
221
222
|
```
|
|
222
223
|
|
|
223
|
-
`ThemeProvider` injects CSS Custom Properties via React 19 style hoisting
|
|
224
|
+
`ThemeProvider` injects CSS Custom Properties via React 19 style hoisting, persists mode to localStorage, and keeps open tabs in sync via the `storage` event. The react entry point ships with `'use client'`, so it can be imported directly from a Next.js App Router layout.
|
|
224
225
|
|
|
225
226
|
### Hooks
|
|
226
227
|
|
|
@@ -242,7 +243,10 @@ import { useResolvedTokens } from '@ttoss/fsl-theme/react';
|
|
|
242
243
|
|
|
243
244
|
// Non-CSS environments (React Native, canvas) — resolved raw values
|
|
244
245
|
const resolved = useResolvedTokens();
|
|
245
|
-
// resolved['semantic.colors.action.primary.background.default'] → '#
|
|
246
|
+
// resolved['semantic.colors.action.primary.background.default'] → '#020617'
|
|
247
|
+
// ⚠ CSS-coupled tokens (spacing steps, fluid text sizes, hit/viewport sizing —
|
|
248
|
+
// see model.md §8) stay as CSS expressions (var()/calc()/clamp()/cqi) and are
|
|
249
|
+
// not usable outside a CSS engine. Colors and scalar tokens resolve fully.
|
|
246
250
|
```
|
|
247
251
|
|
|
248
252
|
### Consuming tokens
|
|
@@ -290,7 +294,9 @@ For one-off custom keys, use `toCssVarName` from `@ttoss/fsl-theme/css` directly
|
|
|
290
294
|
|
|
291
295
|
### React 19 App Router (recommended)
|
|
292
296
|
|
|
293
|
-
`ThemeProvider` with a `theme` prop uses React 19 style hoisting to inject CSS into `<head>` automatically. Only add `ThemeScript` for flash-prevention:
|
|
297
|
+
`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:
|
|
298
|
+
|
|
299
|
+
> **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
300
|
|
|
295
301
|
```tsx
|
|
296
302
|
// app/layout.tsx
|
|
@@ -348,7 +354,7 @@ export default function RootLayout({
|
|
|
348
354
|
}
|
|
349
355
|
```
|
|
350
356
|
|
|
351
|
-
> **Warning:** Do not
|
|
357
|
+
> **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
358
|
|
|
353
359
|
## Dataviz extension
|
|
354
360
|
|
|
@@ -374,16 +380,23 @@ import { getThemeStylesContent } from '@ttoss/fsl-theme/css';
|
|
|
374
380
|
|
|
375
381
|
const css = getThemeStylesContent(myTheme);
|
|
376
382
|
// → :root { --tt-* } + :root[data-tt-mode="dark"] { --tt-* (overrides) }
|
|
383
|
+
// + @media (prefers-color-scheme: dark) fallback for no-JS / pre-hydration
|
|
384
|
+
|
|
385
|
+
// Fixed light/dark default (dark only via explicit toggle)? Skip the OS fallback:
|
|
386
|
+
const lightFirstCss = getThemeStylesContent(myTheme, undefined, {
|
|
387
|
+
systemModeFallback: false,
|
|
388
|
+
});
|
|
389
|
+
// <ThemeProvider> / <ThemeHead> derive this automatically from `defaultMode`.
|
|
377
390
|
```
|
|
378
391
|
|
|
379
392
|
## Storybook / micro-frontends
|
|
380
393
|
|
|
381
|
-
Anchor theme attributes to a specific element instead of `<html
|
|
394
|
+
Anchor theme attributes to a specific element instead of `<html>`. Always pair `root` with a `themeId` — without one, the CSS targets `:root` on `<html>` while the attributes land on the element, and the alternate mode would never apply (a dev warning fires on this combination):
|
|
382
395
|
|
|
383
396
|
```tsx
|
|
384
397
|
const rootRef = React.useRef<HTMLDivElement>(null);
|
|
385
398
|
<div ref={rootRef}>
|
|
386
|
-
<ThemeProvider theme={myTheme} root={rootRef
|
|
399
|
+
<ThemeProvider theme={myTheme} themeId="story" root={rootRef}>
|
|
387
400
|
<Story />
|
|
388
401
|
</ThemeProvider>
|
|
389
402
|
</div>;
|
|
@@ -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
|
/**
|
|
@@ -366,14 +366,18 @@ interface ActionColorStates extends BaseColorStates {
|
|
|
366
366
|
/** Disclosure trigger or menu button is currently open. Use for buttons that own an open popup, menu, or panel. */
|
|
367
367
|
expanded?: CoreColorRef;
|
|
368
368
|
}
|
|
369
|
-
/** `input` context: adds `checked`, `indeterminate`, `pressed`, `expanded
|
|
369
|
+
/** `input` context: adds `checked`, `indeterminate`, `pressed`, `expanded`,
|
|
370
|
+
* and `invalid` for form controls.
|
|
370
371
|
*
|
|
371
|
-
* Validation
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
372
|
+
* Validation doctrine (ADR-017, mirrors `@ttoss/fsl-ui` taxonomy): validation
|
|
373
|
+
* outcome is a **runtime State** (`invalid`), never an authorial Evaluation —
|
|
374
|
+
* evaluation lives in the author's pen, state lives in the user's data.
|
|
375
|
+
* The control itself flips to `input.{role}.*.invalid` when React Aria's
|
|
376
|
+
* `isInvalid` (or a form library) says so; adjacent *display* parts
|
|
377
|
+
* (validationMessage, icon) still consume the `input.negative.*` role — they
|
|
378
|
+
* report valence, the control carries state. The mirror distinction to
|
|
379
|
+
* Lexicon §10.5 (`negative` ≠ `destructive`) is `invalid` (State) ≠
|
|
380
|
+
* `negative` (Evaluation) — Lexicon §10.15.
|
|
377
381
|
*
|
|
378
382
|
* Structural Role → token mapping (FSL Lexicon §2): a part declared with
|
|
379
383
|
* Structural Role `validationMessage` consumes `input.negative.text.*` for
|
|
@@ -393,6 +397,14 @@ interface InputColorStates extends BaseColorStates {
|
|
|
393
397
|
pressed?: CoreColorRef;
|
|
394
398
|
/** Combobox / select / disclosure-style input is currently open. */
|
|
395
399
|
expanded?: CoreColorRef;
|
|
400
|
+
/**
|
|
401
|
+
* Runtime validation outcome — the control's value failed validation.
|
|
402
|
+
* Use when React Aria's `isInvalid` (or a form library) asserts failure.
|
|
403
|
+
* Pair with `input.negative.text.*` on the validationMessage; do not use
|
|
404
|
+
* the `negative` role on the control itself — that is authorial valence
|
|
405
|
+
* (Lexicon §10.15).
|
|
406
|
+
*/
|
|
407
|
+
invalid?: CoreColorRef;
|
|
396
408
|
}
|
|
397
409
|
/** `navigation` context: adds `selected`, `current`, `visited`, `expanded`. */
|
|
398
410
|
interface NavigationColorStates extends BaseColorStates {
|
|
@@ -407,10 +419,10 @@ interface NavigationColorStates extends BaseColorStates {
|
|
|
407
419
|
}
|
|
408
420
|
/** `informational` context: adds `selected`, `visited`, `expanded`.
|
|
409
421
|
*
|
|
410
|
-
* `expanded` covers in-place disclosure on presentational surfaces
|
|
411
|
-
* collapsible panels, expandable cards). `Disclosure`
|
|
412
|
-
*
|
|
413
|
-
*
|
|
422
|
+
* `expanded` covers in-place disclosure on presentational surfaces
|
|
423
|
+
* (accordions, collapsible panels, expandable cards). Note: `Disclosure`
|
|
424
|
+
* Entity Kinds project to `navigation` (ADR-001); `expanded` here serves
|
|
425
|
+
* presentational surfaces that disclose without being Disclosure entities. */
|
|
414
426
|
interface InformationalColorStates extends BaseColorStates {
|
|
415
427
|
/** Presentational element is **one of many** and the user picked it (selectable list row, focused card in a deck). */
|
|
416
428
|
selected?: CoreColorRef;
|
|
@@ -440,7 +452,7 @@ interface FeedbackColorStates {
|
|
|
440
452
|
interface ColorDimensionOf<S extends BaseColorStates> {
|
|
441
453
|
/** Fills and surface backgrounds. Use for any colored area larger than a line. */
|
|
442
454
|
background?: S;
|
|
443
|
-
/** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.
|
|
455
|
+
/** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.border.*` instead — this dimension is colour only. */
|
|
444
456
|
border?: S;
|
|
445
457
|
/** Readable foreground — labels, paragraphs, and text-like icons that inherit `currentColor`. */
|
|
446
458
|
text?: S;
|
|
@@ -587,7 +599,7 @@ interface SemanticElevation {
|
|
|
587
599
|
* Tonal overlay tokens — optional surface color treatments paired with shadows
|
|
588
600
|
* to preserve depth perception in dark or heavily-colored themes.
|
|
589
601
|
*
|
|
590
|
-
* Each token
|
|
602
|
+
* Each token resolves to a color overlay (e.g., `color-mix`, rgba surface).
|
|
591
603
|
* Omit when the product does not use tonal elevation.
|
|
592
604
|
* When present, must cover the same strata that carry visible shadows.
|
|
593
605
|
* @see elevation.md — "Surface + Shadow"
|
|
@@ -595,8 +607,8 @@ interface SemanticElevation {
|
|
|
595
607
|
tonal?: {
|
|
596
608
|
/**
|
|
597
609
|
* Tonal surface treatment paired with `surface.raised`.
|
|
598
|
-
* Use when the raised stratum needs an additional color overlay
|
|
599
|
-
*
|
|
610
|
+
* Use when the raised stratum needs an additional color overlay — dark
|
|
611
|
+
* themes where shadow alone is insufficient.
|
|
600
612
|
* Pair with `surface.raised`; do not use without the matching shadow contract.
|
|
601
613
|
*/
|
|
602
614
|
raised: CoreColorRef;
|
|
@@ -957,13 +969,13 @@ interface GapStackSteps {
|
|
|
957
969
|
interface GapInlineSteps {
|
|
958
970
|
/** Visual-only tight grouping (icon + label inside a single target). Never between focusable targets — use `separation.interactive.min`. */
|
|
959
971
|
xs: CoreSpacingRef;
|
|
960
|
-
/**
|
|
972
|
+
/** Tight inline grouping between related inline siblings. */
|
|
961
973
|
sm: CoreSpacingRef;
|
|
962
|
-
/**
|
|
974
|
+
/** Default inline grouping — pick this when no other step applies. */
|
|
963
975
|
md: CoreSpacingRef;
|
|
964
|
-
/** Spacious inline grouping
|
|
976
|
+
/** Spacious inline grouping. */
|
|
965
977
|
lg: CoreSpacingRef;
|
|
966
|
-
/** Wide inline grouping —
|
|
978
|
+
/** Wide inline grouping — the loosest step before a group break. */
|
|
967
979
|
xl: CoreSpacingRef;
|
|
968
980
|
}
|
|
969
981
|
interface SemanticSpacing {
|
|
@@ -1014,7 +1026,7 @@ interface SemanticSpacing {
|
|
|
1014
1026
|
/**
|
|
1015
1027
|
* Structural outer padding for page-level and section-level layout regions.
|
|
1016
1028
|
* `page` and `section` may use a `clamp()` expression with embedded `{token.path}` refs
|
|
1017
|
-
* (e.g. `clamp({core.
|
|
1029
|
+
* (e.g. `clamp({core.spacing.4}, {core.spacing.6}, {core.spacing.12})`).
|
|
1018
1030
|
* Typed as `RawValue` to allow both simple refs and responsive clamp expressions.
|
|
1019
1031
|
*/
|
|
1020
1032
|
gutter: {
|
|
@@ -1035,7 +1047,7 @@ interface SemanticSpacing {
|
|
|
1035
1047
|
/**
|
|
1036
1048
|
* Ergonomic separation between independently actionable targets in dense clusters.
|
|
1037
1049
|
* May use a `clamp()` expression with an embedded `{token.path}` ref
|
|
1038
|
-
* (e.g. `clamp(8px, {core.
|
|
1050
|
+
* (e.g. `clamp(8px, {core.spacing.2}, 12px)`).
|
|
1039
1051
|
*/
|
|
1040
1052
|
separation: {
|
|
1041
1053
|
interactive: {
|
|
@@ -1368,6 +1380,47 @@ interface ThemeTokens {
|
|
|
1368
1380
|
interface ModeOverride {
|
|
1369
1381
|
semantic: DeepPartial<ThemeTokens['semantic']>;
|
|
1370
1382
|
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Machine-readable theme brief — the design intent a theme declares *before*
|
|
1385
|
+
* token values are chosen (theme-authoring.md §"Theme brief"). It is the home
|
|
1386
|
+
* for the `FSL-DESIGN-001..003` gate (posture, density, accessibility target)
|
|
1387
|
+
* so those rules can be validated rather than living only as prose.
|
|
1388
|
+
*
|
|
1389
|
+
* **Orthogonal to tokens.** The brief lives on `ThemeBundle` alongside `base`,
|
|
1390
|
+
* never inside the token tree — DTCG and CSS emitters read `bundle.base`, so a
|
|
1391
|
+
* brief never affects interchange or CSS output. Optional by design: existing
|
|
1392
|
+
* `createTheme` callers stay valid.
|
|
1393
|
+
*
|
|
1394
|
+
* Allowed values mirror theme-authoring.md §"Allowed values".
|
|
1395
|
+
*/
|
|
1396
|
+
interface ThemeBrief {
|
|
1397
|
+
/** Theme identity name (e.g. `'base'`, `'bruttal'`). */
|
|
1398
|
+
name: string;
|
|
1399
|
+
/** One line on what experience this theme should produce. */
|
|
1400
|
+
purpose?: string;
|
|
1401
|
+
/** Dominant behavioral personality (FSL-DESIGN-001). */
|
|
1402
|
+
primaryPosture: 'calm' | 'productive' | 'technical' | 'expressive' | 'editorial' | 'premium';
|
|
1403
|
+
/** Optional secondary posture; one posture must dominate. */
|
|
1404
|
+
secondaryPosture?: ThemeBrief['primaryPosture'];
|
|
1405
|
+
/** Operational compactness (FSL-DESIGN-002). */
|
|
1406
|
+
densityProfile: 'compact' | 'balanced' | 'comfortable' | 'spacious';
|
|
1407
|
+
/** Reading vs operating vs scanning bias of the interface. */
|
|
1408
|
+
readingMode?: 'reading' | 'operating' | 'scanning' | 'mixed';
|
|
1409
|
+
/** Primary pointer type the hit-target model targets. */
|
|
1410
|
+
pointerProfile?: 'fine' | 'coarse' | 'hybrid';
|
|
1411
|
+
/** Risk profile of adjacent interactions. */
|
|
1412
|
+
interactionRisk?: 'low' | 'medium' | 'high';
|
|
1413
|
+
/** Surface layering strategy. */
|
|
1414
|
+
surfaceModel?: 'flat' | 'lightly-layered' | 'layered' | 'immersive';
|
|
1415
|
+
/** How much brand presence the theme carries. */
|
|
1416
|
+
brandEnergy?: 'quiet' | 'balanced' | 'expressive';
|
|
1417
|
+
/** Non-negotiable accessibility floor (FSL-DESIGN-003). */
|
|
1418
|
+
accessibilityTarget: 'AA' | 'AA+' | 'AAA-like';
|
|
1419
|
+
/** Color-mode support strategy. */
|
|
1420
|
+
colorModeStrategy?: 'light-only' | 'dark-supported' | 'dark-first' | 'adaptive';
|
|
1421
|
+
/** Primary platform the theme is tuned for. */
|
|
1422
|
+
platformBias?: 'web' | 'mobile' | 'desktop' | 'cross-platform';
|
|
1423
|
+
}
|
|
1371
1424
|
/**
|
|
1372
1425
|
* A theme bundle packages a complete `ThemeTokens` (the base)
|
|
1373
1426
|
* with an optional semantic-only override for the alternate color mode.
|
|
@@ -1413,6 +1466,13 @@ interface ThemeBundle {
|
|
|
1413
1466
|
* Only semantic references that differ need to be listed — core tokens are shared.
|
|
1414
1467
|
*/
|
|
1415
1468
|
alternate?: ModeOverride;
|
|
1469
|
+
/**
|
|
1470
|
+
* Machine-readable design brief (posture, density, accessibility target, …).
|
|
1471
|
+
* Orthogonal to tokens — never serialized into DTCG/CSS. Optional so existing
|
|
1472
|
+
* callers stay valid; carrying one lets `FSL-DESIGN-001..003` be enforced.
|
|
1473
|
+
* @see {@link ThemeBrief}
|
|
1474
|
+
*/
|
|
1475
|
+
meta?: ThemeBrief;
|
|
1416
1476
|
}
|
|
1417
1477
|
/**
|
|
1418
1478
|
* The semantic token layer of a theme. This is the **only** part of the token
|
|
@@ -1424,4 +1484,4 @@ interface ThemeBundle {
|
|
|
1424
1484
|
*/
|
|
1425
1485
|
type SemanticTokens = ThemeTokens['semantic'];
|
|
1426
1486
|
//#endregion
|
|
1427
|
-
export {
|
|
1487
|
+
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
|
/**
|
|
@@ -366,14 +366,18 @@ interface ActionColorStates extends BaseColorStates {
|
|
|
366
366
|
/** Disclosure trigger or menu button is currently open. Use for buttons that own an open popup, menu, or panel. */
|
|
367
367
|
expanded?: CoreColorRef;
|
|
368
368
|
}
|
|
369
|
-
/** `input` context: adds `checked`, `indeterminate`, `pressed`, `expanded
|
|
369
|
+
/** `input` context: adds `checked`, `indeterminate`, `pressed`, `expanded`,
|
|
370
|
+
* and `invalid` for form controls.
|
|
370
371
|
*
|
|
371
|
-
* Validation
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
372
|
+
* Validation doctrine (ADR-017, mirrors `@ttoss/fsl-ui` taxonomy): validation
|
|
373
|
+
* outcome is a **runtime State** (`invalid`), never an authorial Evaluation —
|
|
374
|
+
* evaluation lives in the author's pen, state lives in the user's data.
|
|
375
|
+
* The control itself flips to `input.{role}.*.invalid` when React Aria's
|
|
376
|
+
* `isInvalid` (or a form library) says so; adjacent *display* parts
|
|
377
|
+
* (validationMessage, icon) still consume the `input.negative.*` role — they
|
|
378
|
+
* report valence, the control carries state. The mirror distinction to
|
|
379
|
+
* Lexicon §10.5 (`negative` ≠ `destructive`) is `invalid` (State) ≠
|
|
380
|
+
* `negative` (Evaluation) — Lexicon §10.15.
|
|
377
381
|
*
|
|
378
382
|
* Structural Role → token mapping (FSL Lexicon §2): a part declared with
|
|
379
383
|
* Structural Role `validationMessage` consumes `input.negative.text.*` for
|
|
@@ -393,6 +397,14 @@ interface InputColorStates extends BaseColorStates {
|
|
|
393
397
|
pressed?: CoreColorRef;
|
|
394
398
|
/** Combobox / select / disclosure-style input is currently open. */
|
|
395
399
|
expanded?: CoreColorRef;
|
|
400
|
+
/**
|
|
401
|
+
* Runtime validation outcome — the control's value failed validation.
|
|
402
|
+
* Use when React Aria's `isInvalid` (or a form library) asserts failure.
|
|
403
|
+
* Pair with `input.negative.text.*` on the validationMessage; do not use
|
|
404
|
+
* the `negative` role on the control itself — that is authorial valence
|
|
405
|
+
* (Lexicon §10.15).
|
|
406
|
+
*/
|
|
407
|
+
invalid?: CoreColorRef;
|
|
396
408
|
}
|
|
397
409
|
/** `navigation` context: adds `selected`, `current`, `visited`, `expanded`. */
|
|
398
410
|
interface NavigationColorStates extends BaseColorStates {
|
|
@@ -407,10 +419,10 @@ interface NavigationColorStates extends BaseColorStates {
|
|
|
407
419
|
}
|
|
408
420
|
/** `informational` context: adds `selected`, `visited`, `expanded`.
|
|
409
421
|
*
|
|
410
|
-
* `expanded` covers in-place disclosure on presentational surfaces
|
|
411
|
-
* collapsible panels, expandable cards). `Disclosure`
|
|
412
|
-
*
|
|
413
|
-
*
|
|
422
|
+
* `expanded` covers in-place disclosure on presentational surfaces
|
|
423
|
+
* (accordions, collapsible panels, expandable cards). Note: `Disclosure`
|
|
424
|
+
* Entity Kinds project to `navigation` (ADR-001); `expanded` here serves
|
|
425
|
+
* presentational surfaces that disclose without being Disclosure entities. */
|
|
414
426
|
interface InformationalColorStates extends BaseColorStates {
|
|
415
427
|
/** Presentational element is **one of many** and the user picked it (selectable list row, focused card in a deck). */
|
|
416
428
|
selected?: CoreColorRef;
|
|
@@ -440,7 +452,7 @@ interface FeedbackColorStates {
|
|
|
440
452
|
interface ColorDimensionOf<S extends BaseColorStates> {
|
|
441
453
|
/** Fills and surface backgrounds. Use for any colored area larger than a line. */
|
|
442
454
|
background?: S;
|
|
443
|
-
/** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.
|
|
455
|
+
/** Outlines, separators, rings, and other line-color pairings. For line *geometry* (width, style) consume `semantic.border.*` instead — this dimension is colour only. */
|
|
444
456
|
border?: S;
|
|
445
457
|
/** Readable foreground — labels, paragraphs, and text-like icons that inherit `currentColor`. */
|
|
446
458
|
text?: S;
|
|
@@ -587,7 +599,7 @@ interface SemanticElevation {
|
|
|
587
599
|
* Tonal overlay tokens — optional surface color treatments paired with shadows
|
|
588
600
|
* to preserve depth perception in dark or heavily-colored themes.
|
|
589
601
|
*
|
|
590
|
-
* Each token
|
|
602
|
+
* Each token resolves to a color overlay (e.g., `color-mix`, rgba surface).
|
|
591
603
|
* Omit when the product does not use tonal elevation.
|
|
592
604
|
* When present, must cover the same strata that carry visible shadows.
|
|
593
605
|
* @see elevation.md — "Surface + Shadow"
|
|
@@ -595,8 +607,8 @@ interface SemanticElevation {
|
|
|
595
607
|
tonal?: {
|
|
596
608
|
/**
|
|
597
609
|
* Tonal surface treatment paired with `surface.raised`.
|
|
598
|
-
* Use when the raised stratum needs an additional color overlay
|
|
599
|
-
*
|
|
610
|
+
* Use when the raised stratum needs an additional color overlay — dark
|
|
611
|
+
* themes where shadow alone is insufficient.
|
|
600
612
|
* Pair with `surface.raised`; do not use without the matching shadow contract.
|
|
601
613
|
*/
|
|
602
614
|
raised: CoreColorRef;
|
|
@@ -957,13 +969,13 @@ interface GapStackSteps {
|
|
|
957
969
|
interface GapInlineSteps {
|
|
958
970
|
/** Visual-only tight grouping (icon + label inside a single target). Never between focusable targets — use `separation.interactive.min`. */
|
|
959
971
|
xs: CoreSpacingRef;
|
|
960
|
-
/**
|
|
972
|
+
/** Tight inline grouping between related inline siblings. */
|
|
961
973
|
sm: CoreSpacingRef;
|
|
962
|
-
/**
|
|
974
|
+
/** Default inline grouping — pick this when no other step applies. */
|
|
963
975
|
md: CoreSpacingRef;
|
|
964
|
-
/** Spacious inline grouping
|
|
976
|
+
/** Spacious inline grouping. */
|
|
965
977
|
lg: CoreSpacingRef;
|
|
966
|
-
/** Wide inline grouping —
|
|
978
|
+
/** Wide inline grouping — the loosest step before a group break. */
|
|
967
979
|
xl: CoreSpacingRef;
|
|
968
980
|
}
|
|
969
981
|
interface SemanticSpacing {
|
|
@@ -1014,7 +1026,7 @@ interface SemanticSpacing {
|
|
|
1014
1026
|
/**
|
|
1015
1027
|
* Structural outer padding for page-level and section-level layout regions.
|
|
1016
1028
|
* `page` and `section` may use a `clamp()` expression with embedded `{token.path}` refs
|
|
1017
|
-
* (e.g. `clamp({core.
|
|
1029
|
+
* (e.g. `clamp({core.spacing.4}, {core.spacing.6}, {core.spacing.12})`).
|
|
1018
1030
|
* Typed as `RawValue` to allow both simple refs and responsive clamp expressions.
|
|
1019
1031
|
*/
|
|
1020
1032
|
gutter: {
|
|
@@ -1035,7 +1047,7 @@ interface SemanticSpacing {
|
|
|
1035
1047
|
/**
|
|
1036
1048
|
* Ergonomic separation between independently actionable targets in dense clusters.
|
|
1037
1049
|
* May use a `clamp()` expression with an embedded `{token.path}` ref
|
|
1038
|
-
* (e.g. `clamp(8px, {core.
|
|
1050
|
+
* (e.g. `clamp(8px, {core.spacing.2}, 12px)`).
|
|
1039
1051
|
*/
|
|
1040
1052
|
separation: {
|
|
1041
1053
|
interactive: {
|
|
@@ -1368,6 +1380,47 @@ interface ThemeTokens {
|
|
|
1368
1380
|
interface ModeOverride {
|
|
1369
1381
|
semantic: DeepPartial<ThemeTokens['semantic']>;
|
|
1370
1382
|
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Machine-readable theme brief — the design intent a theme declares *before*
|
|
1385
|
+
* token values are chosen (theme-authoring.md §"Theme brief"). It is the home
|
|
1386
|
+
* for the `FSL-DESIGN-001..003` gate (posture, density, accessibility target)
|
|
1387
|
+
* so those rules can be validated rather than living only as prose.
|
|
1388
|
+
*
|
|
1389
|
+
* **Orthogonal to tokens.** The brief lives on `ThemeBundle` alongside `base`,
|
|
1390
|
+
* never inside the token tree — DTCG and CSS emitters read `bundle.base`, so a
|
|
1391
|
+
* brief never affects interchange or CSS output. Optional by design: existing
|
|
1392
|
+
* `createTheme` callers stay valid.
|
|
1393
|
+
*
|
|
1394
|
+
* Allowed values mirror theme-authoring.md §"Allowed values".
|
|
1395
|
+
*/
|
|
1396
|
+
interface ThemeBrief {
|
|
1397
|
+
/** Theme identity name (e.g. `'base'`, `'bruttal'`). */
|
|
1398
|
+
name: string;
|
|
1399
|
+
/** One line on what experience this theme should produce. */
|
|
1400
|
+
purpose?: string;
|
|
1401
|
+
/** Dominant behavioral personality (FSL-DESIGN-001). */
|
|
1402
|
+
primaryPosture: 'calm' | 'productive' | 'technical' | 'expressive' | 'editorial' | 'premium';
|
|
1403
|
+
/** Optional secondary posture; one posture must dominate. */
|
|
1404
|
+
secondaryPosture?: ThemeBrief['primaryPosture'];
|
|
1405
|
+
/** Operational compactness (FSL-DESIGN-002). */
|
|
1406
|
+
densityProfile: 'compact' | 'balanced' | 'comfortable' | 'spacious';
|
|
1407
|
+
/** Reading vs operating vs scanning bias of the interface. */
|
|
1408
|
+
readingMode?: 'reading' | 'operating' | 'scanning' | 'mixed';
|
|
1409
|
+
/** Primary pointer type the hit-target model targets. */
|
|
1410
|
+
pointerProfile?: 'fine' | 'coarse' | 'hybrid';
|
|
1411
|
+
/** Risk profile of adjacent interactions. */
|
|
1412
|
+
interactionRisk?: 'low' | 'medium' | 'high';
|
|
1413
|
+
/** Surface layering strategy. */
|
|
1414
|
+
surfaceModel?: 'flat' | 'lightly-layered' | 'layered' | 'immersive';
|
|
1415
|
+
/** How much brand presence the theme carries. */
|
|
1416
|
+
brandEnergy?: 'quiet' | 'balanced' | 'expressive';
|
|
1417
|
+
/** Non-negotiable accessibility floor (FSL-DESIGN-003). */
|
|
1418
|
+
accessibilityTarget: 'AA' | 'AA+' | 'AAA-like';
|
|
1419
|
+
/** Color-mode support strategy. */
|
|
1420
|
+
colorModeStrategy?: 'light-only' | 'dark-supported' | 'dark-first' | 'adaptive';
|
|
1421
|
+
/** Primary platform the theme is tuned for. */
|
|
1422
|
+
platformBias?: 'web' | 'mobile' | 'desktop' | 'cross-platform';
|
|
1423
|
+
}
|
|
1371
1424
|
/**
|
|
1372
1425
|
* A theme bundle packages a complete `ThemeTokens` (the base)
|
|
1373
1426
|
* with an optional semantic-only override for the alternate color mode.
|
|
@@ -1413,6 +1466,13 @@ interface ThemeBundle {
|
|
|
1413
1466
|
* Only semantic references that differ need to be listed — core tokens are shared.
|
|
1414
1467
|
*/
|
|
1415
1468
|
alternate?: ModeOverride;
|
|
1469
|
+
/**
|
|
1470
|
+
* Machine-readable design brief (posture, density, accessibility target, …).
|
|
1471
|
+
* Orthogonal to tokens — never serialized into DTCG/CSS. Optional so existing
|
|
1472
|
+
* callers stay valid; carrying one lets `FSL-DESIGN-001..003` be enforced.
|
|
1473
|
+
* @see {@link ThemeBrief}
|
|
1474
|
+
*/
|
|
1475
|
+
meta?: ThemeBrief;
|
|
1416
1476
|
}
|
|
1417
1477
|
/**
|
|
1418
1478
|
* The semantic token layer of a theme. This is the **only** part of the token
|
|
@@ -1424,4 +1484,4 @@ interface ThemeBundle {
|
|
|
1424
1484
|
*/
|
|
1425
1485
|
type SemanticTokens = ThemeTokens['semantic'];
|
|
1426
1486
|
//#endregion
|
|
1427
|
-
export {
|
|
1487
|
+
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
|
-
import {
|
|
2
|
+
import { a as darkAlternate, i as baseTheme, n as createTheme, r as baseBrief } from "./createTheme-Bm9LVQ4w.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_createTheme = require('./createTheme-
|
|
2
|
+
const require_createTheme = require('./createTheme-B7H0xSVp.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
|