@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
-
import {
|
|
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
|
/**
|
|
@@ -7,6 +7,33 @@ import { c as validateRefs, r as deepMerge } from "./helpers-CA4Qkxct.mjs";
|
|
|
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
|
+
} = 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(COMPOUND_REF_RE.source, 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
|
|
@@ -1783,20 +1900,25 @@ 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
|
|
1802
|
-
export { darkAlternate as i, createTheme as n,
|
|
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-
|
|
6
|
-
const require_toCssVars = require('./toCssVars-
|
|
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
package/dist/css.d.mts
CHANGED
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-
|
|
3
|
-
import { n as toCssVars, t as toCssVarName } from "./toCssVars-
|
|
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
|
/**
|
package/dist/dataviz/index.cjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, {
|
|
3
3
|
value: 'Module'
|
|
4
4
|
});
|
|
5
|
-
const require_react = require('../react-
|
|
6
|
-
const require_withDataviz = require('../withDataviz-
|
|
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
|
/**
|
package/dist/dataviz/index.d.cts
CHANGED
package/dist/dataviz/index.d.mts
CHANGED
package/dist/dataviz/index.mjs
CHANGED
|
@@ -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-
|
|
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
|
package/dist/dtcg.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-
|
|
6
|
-
const require_tokenRegistry = require('./tokenRegistry-
|
|
5
|
+
const require_helpers = require('./helpers-BU40JvFA.cjs');
|
|
6
|
+
const require_tokenRegistry = require('./tokenRegistry-f_Bx_BAN.cjs');
|
|
7
7
|
|
|
8
8
|
//#region src/roots/toDTCG.ts
|
|
9
9
|
/**
|
|
@@ -26,12 +26,20 @@ const require_tokenRegistry = require('./tokenRegistry-B9zMRjpL.cjs');
|
|
|
26
26
|
*
|
|
27
27
|
* Motion leaves (SemanticMotionEntry) overridden separately:
|
|
28
28
|
* - `.duration` → 'duration'
|
|
29
|
+
*
|
|
30
|
+
* Border / focus line widths resolve to a `dimension` (`1px`, `2px`) but sit
|
|
31
|
+
* under the untyped `semantic.border.` / `semantic.focus.` prefixes:
|
|
32
|
+
* - `.width` → 'dimension'
|
|
33
|
+
*/
|
|
34
|
+
var SUFFIX_TYPE_OVERRIDES = [[".duration", "duration"], [".fontFamily", "fontFamily"], [".fontSize", "dimension"], [".fontWeight", "fontWeight"], [".lineHeight", "number"], [".letterSpacing", "dimension"], [".width", "dimension"], [".ring.color", "color"]];
|
|
35
|
+
/**
|
|
36
|
+
* Infer the DTCG `$type` for a token path, or `undefined` when the token is
|
|
37
|
+
* opaque (keyword/curve/dash-string) and has no valid DTCG scalar type — in
|
|
38
|
+
* which case `toDTCG` omits `$type` rather than emitting an invalid one.
|
|
29
39
|
*/
|
|
30
|
-
var
|
|
31
|
-
var inferType = path => {
|
|
40
|
+
var inferDtcgType = path => {
|
|
32
41
|
for (const [suffix, type] of SUFFIX_TYPE_OVERRIDES) if (path.endsWith(suffix)) return type;
|
|
33
42
|
for (const [prefix, type] of require_tokenRegistry.DTCG_TYPE_PREFIXES) if (path.startsWith(prefix)) return type;
|
|
34
|
-
return "string";
|
|
35
43
|
};
|
|
36
44
|
/**
|
|
37
45
|
* Set a value at a dot-path in a nested object, creating intermediate
|
|
@@ -71,8 +79,16 @@ var buildHitExtension = (step, theme) => {
|
|
|
71
79
|
* Root 3 — W3C Design Tokens (DTCG JSON).
|
|
72
80
|
*
|
|
73
81
|
* Convert a `ThemeTokens` into a structured token tree following the
|
|
74
|
-
* W3C Design Tokens Community Group format. Every leaf
|
|
75
|
-
*
|
|
82
|
+
* W3C Design Tokens Community Group format (2025.10). Every leaf has a
|
|
83
|
+
* `$value`; `$type` is inferred from the token path and **omitted** for opaque
|
|
84
|
+
* tokens with no valid DTCG scalar type (`$type` is optional per spec).
|
|
85
|
+
*
|
|
86
|
+
* **Profile:** this emits **fully-resolved scalar tokens** — `$value`s are
|
|
87
|
+
* concrete (no `{alias}` refs) and composite shapes (typography, shadow,
|
|
88
|
+
* border, transition) are emitted as their individual scalar leaves, not as
|
|
89
|
+
* DTCG composite objects. This is a conformant DTCG profile; richer
|
|
90
|
+
* alias-preserving / composite output is a deferred enhancement (see
|
|
91
|
+
* CONTRIBUTING.md ADR-013).
|
|
76
92
|
*
|
|
77
93
|
* Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
|
|
78
94
|
* field declaring their coarse-pointer override value, so non-CSS consumers
|
|
@@ -98,10 +114,11 @@ var toDTCG = theme => {
|
|
|
98
114
|
const flat = require_helpers.toFlatTokens(theme);
|
|
99
115
|
const tree = {};
|
|
100
116
|
for (const [path, value] of Object.entries(flat)) {
|
|
117
|
+
const dtcgType = inferDtcgType(path);
|
|
101
118
|
const token = {
|
|
102
|
-
$value: value
|
|
103
|
-
$type: inferType(path)
|
|
119
|
+
$value: value
|
|
104
120
|
};
|
|
121
|
+
if (dtcgType !== void 0) token.$type = dtcgType;
|
|
105
122
|
if (path.startsWith(SEMANTIC_HIT_PREFIX)) {
|
|
106
123
|
const ext = buildHitExtension(path.slice(20), theme);
|
|
107
124
|
if (ext) token.$extensions = ext;
|
package/dist/dtcg.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { a as ThemeTokens } from "./Types-BkaxtsMv.cjs";
|
|
3
3
|
|
|
4
4
|
//#region src/roots/toDTCG.d.ts
|
|
5
5
|
/**
|
|
@@ -10,7 +10,12 @@ import { i as ThemeTokens } from "./Types-BiBa17RL.cjs";
|
|
|
10
10
|
*/
|
|
11
11
|
interface DTCGToken {
|
|
12
12
|
$value: string | number;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* W3C DTCG type. **Optional** — omitted for opaque/keyword tokens that have
|
|
15
|
+
* no valid DTCG scalar type (`$type` is optional in the DTCG spec). There is
|
|
16
|
+
* no `'string'` DTCG type; emitting one would break conformant importers.
|
|
17
|
+
*/
|
|
18
|
+
$type?: string;
|
|
14
19
|
$extensions?: Record<string, unknown>;
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
@@ -23,8 +28,16 @@ type DTCGTokenTree = DTCGToken | {
|
|
|
23
28
|
* Root 3 — W3C Design Tokens (DTCG JSON).
|
|
24
29
|
*
|
|
25
30
|
* Convert a `ThemeTokens` into a structured token tree following the
|
|
26
|
-
* W3C Design Tokens Community Group format. Every leaf
|
|
27
|
-
*
|
|
31
|
+
* W3C Design Tokens Community Group format (2025.10). Every leaf has a
|
|
32
|
+
* `$value`; `$type` is inferred from the token path and **omitted** for opaque
|
|
33
|
+
* tokens with no valid DTCG scalar type (`$type` is optional per spec).
|
|
34
|
+
*
|
|
35
|
+
* **Profile:** this emits **fully-resolved scalar tokens** — `$value`s are
|
|
36
|
+
* concrete (no `{alias}` refs) and composite shapes (typography, shadow,
|
|
37
|
+
* border, transition) are emitted as their individual scalar leaves, not as
|
|
38
|
+
* DTCG composite objects. This is a conformant DTCG profile; richer
|
|
39
|
+
* alias-preserving / composite output is a deferred enhancement (see
|
|
40
|
+
* CONTRIBUTING.md ADR-013).
|
|
28
41
|
*
|
|
29
42
|
* Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
|
|
30
43
|
* field declaring their coarse-pointer override value, so non-CSS consumers
|
package/dist/dtcg.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { a as ThemeTokens } from "./Types-BkaxtsMv.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/roots/toDTCG.d.ts
|
|
5
5
|
/**
|
|
@@ -10,7 +10,12 @@ import { i as ThemeTokens } from "./Types-BiBa17RL.mjs";
|
|
|
10
10
|
*/
|
|
11
11
|
interface DTCGToken {
|
|
12
12
|
$value: string | number;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* W3C DTCG type. **Optional** — omitted for opaque/keyword tokens that have
|
|
15
|
+
* no valid DTCG scalar type (`$type` is optional in the DTCG spec). There is
|
|
16
|
+
* no `'string'` DTCG type; emitting one would break conformant importers.
|
|
17
|
+
*/
|
|
18
|
+
$type?: string;
|
|
14
19
|
$extensions?: Record<string, unknown>;
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
@@ -23,8 +28,16 @@ type DTCGTokenTree = DTCGToken | {
|
|
|
23
28
|
* Root 3 — W3C Design Tokens (DTCG JSON).
|
|
24
29
|
*
|
|
25
30
|
* Convert a `ThemeTokens` into a structured token tree following the
|
|
26
|
-
* W3C Design Tokens Community Group format. Every leaf
|
|
27
|
-
*
|
|
31
|
+
* W3C Design Tokens Community Group format (2025.10). Every leaf has a
|
|
32
|
+
* `$value`; `$type` is inferred from the token path and **omitted** for opaque
|
|
33
|
+
* tokens with no valid DTCG scalar type (`$type` is optional per spec).
|
|
34
|
+
*
|
|
35
|
+
* **Profile:** this emits **fully-resolved scalar tokens** — `$value`s are
|
|
36
|
+
* concrete (no `{alias}` refs) and composite shapes (typography, shadow,
|
|
37
|
+
* border, transition) are emitted as their individual scalar leaves, not as
|
|
38
|
+
* DTCG composite objects. This is a conformant DTCG profile; richer
|
|
39
|
+
* alias-preserving / composite output is a deferred enhancement (see
|
|
40
|
+
* CONTRIBUTING.md ADR-013).
|
|
28
41
|
*
|
|
29
42
|
* Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
|
|
30
43
|
* field declaring their coarse-pointer override value, so non-CSS consumers
|
package/dist/dtcg.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-
|
|
3
|
-
import { n as DTCG_TYPE_PREFIXES } from "./tokenRegistry-
|
|
2
|
+
import { s as toFlatTokens } from "./helpers-BXaKe2s3.mjs";
|
|
3
|
+
import { n as DTCG_TYPE_PREFIXES } from "./tokenRegistry-DviyqDeE.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/roots/toDTCG.ts
|
|
6
6
|
/**
|
|
@@ -23,12 +23,20 @@ import { n as DTCG_TYPE_PREFIXES } from "./tokenRegistry-DolhB5Vf.mjs";
|
|
|
23
23
|
*
|
|
24
24
|
* Motion leaves (SemanticMotionEntry) overridden separately:
|
|
25
25
|
* - `.duration` → 'duration'
|
|
26
|
+
*
|
|
27
|
+
* Border / focus line widths resolve to a `dimension` (`1px`, `2px`) but sit
|
|
28
|
+
* under the untyped `semantic.border.` / `semantic.focus.` prefixes:
|
|
29
|
+
* - `.width` → 'dimension'
|
|
30
|
+
*/
|
|
31
|
+
var SUFFIX_TYPE_OVERRIDES = [[".duration", "duration"], [".fontFamily", "fontFamily"], [".fontSize", "dimension"], [".fontWeight", "fontWeight"], [".lineHeight", "number"], [".letterSpacing", "dimension"], [".width", "dimension"], [".ring.color", "color"]];
|
|
32
|
+
/**
|
|
33
|
+
* Infer the DTCG `$type` for a token path, or `undefined` when the token is
|
|
34
|
+
* opaque (keyword/curve/dash-string) and has no valid DTCG scalar type — in
|
|
35
|
+
* which case `toDTCG` omits `$type` rather than emitting an invalid one.
|
|
26
36
|
*/
|
|
27
|
-
var
|
|
28
|
-
var inferType = path => {
|
|
37
|
+
var inferDtcgType = path => {
|
|
29
38
|
for (const [suffix, type] of SUFFIX_TYPE_OVERRIDES) if (path.endsWith(suffix)) return type;
|
|
30
39
|
for (const [prefix, type] of DTCG_TYPE_PREFIXES) if (path.startsWith(prefix)) return type;
|
|
31
|
-
return "string";
|
|
32
40
|
};
|
|
33
41
|
/**
|
|
34
42
|
* Set a value at a dot-path in a nested object, creating intermediate
|
|
@@ -68,8 +76,16 @@ var buildHitExtension = (step, theme) => {
|
|
|
68
76
|
* Root 3 — W3C Design Tokens (DTCG JSON).
|
|
69
77
|
*
|
|
70
78
|
* Convert a `ThemeTokens` into a structured token tree following the
|
|
71
|
-
* W3C Design Tokens Community Group format. Every leaf
|
|
72
|
-
*
|
|
79
|
+
* W3C Design Tokens Community Group format (2025.10). Every leaf has a
|
|
80
|
+
* `$value`; `$type` is inferred from the token path and **omitted** for opaque
|
|
81
|
+
* tokens with no valid DTCG scalar type (`$type` is optional per spec).
|
|
82
|
+
*
|
|
83
|
+
* **Profile:** this emits **fully-resolved scalar tokens** — `$value`s are
|
|
84
|
+
* concrete (no `{alias}` refs) and composite shapes (typography, shadow,
|
|
85
|
+
* border, transition) are emitted as their individual scalar leaves, not as
|
|
86
|
+
* DTCG composite objects. This is a conformant DTCG profile; richer
|
|
87
|
+
* alias-preserving / composite output is a deferred enhancement (see
|
|
88
|
+
* CONTRIBUTING.md ADR-013).
|
|
73
89
|
*
|
|
74
90
|
* Semantic hit tokens (`semantic.sizing.hit.*`) include a `$extensions`
|
|
75
91
|
* field declaring their coarse-pointer override value, so non-CSS consumers
|
|
@@ -95,10 +111,11 @@ var toDTCG = theme => {
|
|
|
95
111
|
const flat = toFlatTokens(theme);
|
|
96
112
|
const tree = {};
|
|
97
113
|
for (const [path, value] of Object.entries(flat)) {
|
|
114
|
+
const dtcgType = inferDtcgType(path);
|
|
98
115
|
const token = {
|
|
99
|
-
$value: value
|
|
100
|
-
$type: inferType(path)
|
|
116
|
+
$value: value
|
|
101
117
|
};
|
|
118
|
+
if (dtcgType !== void 0) token.$type = dtcgType;
|
|
102
119
|
if (path.startsWith(SEMANTIC_HIT_PREFIX)) {
|
|
103
120
|
const ext = buildHitExtension(path.slice(20), theme);
|
|
104
121
|
if (ext) token.$extensions = ext;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
2
|
//#region src/roots/helpers.ts
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Check if a value is a **pure** token reference like `{core.colors.brand.500}`.
|
|
5
|
+
*
|
|
6
|
+
* A pure ref is a single `{path}` with no interior braces. Compound expressions
|
|
7
|
+
* (`clamp({a}, {b})`) and multi-ref strings (`{a} {b}`) are NOT pure refs — they
|
|
8
|
+
* start `{`/end `}` but carry interior braces, and must go through the compound
|
|
9
|
+
* resolution path instead. Without the interior-brace check `{a} {b}` would be
|
|
10
|
+
* mis-parsed as the single path `a} {b`.
|
|
11
|
+
*/
|
|
12
|
+
var hasInteriorBraces = value => {
|
|
13
|
+
return value.indexOf("{", 1) !== -1 || value.indexOf("}") !== value.length - 1;
|
|
14
|
+
};
|
|
4
15
|
var isTokenRef = value => {
|
|
5
|
-
return typeof value === "string" && value.length > 2 && value.startsWith("{") && value.endsWith("}");
|
|
16
|
+
return typeof value === "string" && value.length > 2 && value.startsWith("{") && value.endsWith("}") && !hasInteriorBraces(value);
|
|
6
17
|
};
|
|
7
18
|
/** Extract the inner path from a token reference: `{core.colors.brand.500}` → `core.colors.brand.500` */
|
|
8
19
|
var extractRefPath = ref => {
|
|
@@ -24,6 +35,14 @@ var isPlainObject = value => {
|
|
|
24
35
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
36
|
};
|
|
26
37
|
/**
|
|
38
|
+
* Prototype-chain keys that must never be written from override data —
|
|
39
|
+
* `result['__proto__']` reaches the prototype setter, not an own property.
|
|
40
|
+
* Guards `createTheme`'s public `overrides` against prototype pollution.
|
|
41
|
+
*/
|
|
42
|
+
var isUnsafeMergeKey = key => {
|
|
43
|
+
return key === "__proto__" || key === "constructor" || key === "prototype";
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
27
46
|
* Recursively merges `overrides` into `base`.
|
|
28
47
|
* - Plain objects are merged recursively.
|
|
29
48
|
* - All other values (primitives, arrays) are replaced.
|
|
@@ -34,6 +53,7 @@ var deepMerge = (base, overrides) => {
|
|
|
34
53
|
...base
|
|
35
54
|
};
|
|
36
55
|
for (const key of Object.keys(overrides)) {
|
|
56
|
+
if (isUnsafeMergeKey(key)) continue;
|
|
37
57
|
const baseVal = result[key];
|
|
38
58
|
const overVal = overrides[key];
|
|
39
59
|
if (overVal === void 0) continue;
|
|
@@ -69,73 +89,6 @@ var flattenTheme = theme => {
|
|
|
69
89
|
};
|
|
70
90
|
};
|
|
71
91
|
/**
|
|
72
|
-
* Compute the Levenshtein edit distance between two strings.
|
|
73
|
-
* Used exclusively by `validateRefs` to power "did you mean?" suggestions.
|
|
74
|
-
*/
|
|
75
|
-
var levenshtein = (a, b) => {
|
|
76
|
-
const m = a.length;
|
|
77
|
-
const n = b.length;
|
|
78
|
-
const dp = Array.from({
|
|
79
|
-
length: m + 1
|
|
80
|
-
}, () => {
|
|
81
|
-
return Array.from({
|
|
82
|
-
length: n + 1
|
|
83
|
-
}, () => {
|
|
84
|
-
return 0;
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
88
|
-
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
89
|
-
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]);
|
|
90
|
-
return dp[m][n];
|
|
91
|
-
};
|
|
92
|
-
/**
|
|
93
|
-
* Validate that every `{ref}` in the merged theme points to an existing path.
|
|
94
|
-
* Emits `console.warn` for each broken reference with a "did you mean?" suggestion.
|
|
95
|
-
*
|
|
96
|
-
* **DEV-only** — callers gate this behind `process.env.NODE_ENV !== 'production'`
|
|
97
|
-
* so bundlers tree-shake the entire call in production builds.
|
|
98
|
-
*/
|
|
99
|
-
var validateRefs = theme => {
|
|
100
|
-
const {
|
|
101
|
-
core,
|
|
102
|
-
semantic
|
|
103
|
-
} = flattenTheme(theme);
|
|
104
|
-
const all = {
|
|
105
|
-
...core,
|
|
106
|
-
...semantic
|
|
107
|
-
};
|
|
108
|
-
const allKeys = Object.keys(all);
|
|
109
|
-
const findSuggestion = (refPath, candidates) => {
|
|
110
|
-
if (candidates.length === 0) return "";
|
|
111
|
-
let bestDist = Infinity;
|
|
112
|
-
let bestKey = "";
|
|
113
|
-
for (const candidate of candidates) {
|
|
114
|
-
const dist = levenshtein(refPath, candidate);
|
|
115
|
-
if (dist < bestDist) {
|
|
116
|
-
bestDist = dist;
|
|
117
|
-
bestKey = candidate;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (bestDist <= Math.ceil(refPath.length * .4)) return `\n Did you mean '{${bestKey}}'?`;
|
|
121
|
-
return "";
|
|
122
|
-
};
|
|
123
|
-
for (const [ownerKey, value] of Object.entries(all)) {
|
|
124
|
-
if (typeof value !== "string" || !value.includes("{")) continue;
|
|
125
|
-
let match;
|
|
126
|
-
const re = new RegExp(COMPOUND_REF_RE.source, COMPOUND_REF_RE.flags);
|
|
127
|
-
while ((match = re.exec(value)) !== null) {
|
|
128
|
-
const refPath = match[1];
|
|
129
|
-
if (all[refPath] !== void 0) continue;
|
|
130
|
-
const prefix = refPath.split(".")[0];
|
|
131
|
-
const suggestion = findSuggestion(refPath, allKeys.filter(k => {
|
|
132
|
-
return k.startsWith(prefix + ".");
|
|
133
|
-
}));
|
|
134
|
-
console.warn(`[fsl-theme] Invalid token reference '{${refPath}}' at path '${ownerKey}'.${suggestion}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
/**
|
|
139
92
|
* Flatten a `ThemeTokens` into a `Record<string, string | number>` with
|
|
140
93
|
* every `{ref}` recursively resolved to its final raw value where possible.
|
|
141
94
|
*
|
|
@@ -181,8 +134,8 @@ var toFlatTokens = (theme, options = {}) => {
|
|
|
181
134
|
/**
|
|
182
135
|
* Resolve all embedded `{path}` refs in a raw string expression.
|
|
183
136
|
*
|
|
184
|
-
* Handles both pure refs (`{core.
|
|
185
|
-
* (`clamp({core.
|
|
137
|
+
* Handles both pure refs (`{core.spacing.4}`) and compound expressions
|
|
138
|
+
* (`clamp({core.spacing.4}, {core.spacing.6}, {core.spacing.12})`).
|
|
186
139
|
*/
|
|
187
140
|
const resolveInline = (value, seen, ownerKey) => {
|
|
188
141
|
return value.replace(COMPOUND_REF_RE, (_match, path) => {
|
|
@@ -195,7 +148,9 @@ var toFlatTokens = (theme, options = {}) => {
|
|
|
195
148
|
reportUnresolved(ownerKey, path, "circular reference");
|
|
196
149
|
return `{${path}}`;
|
|
197
150
|
}
|
|
198
|
-
const
|
|
151
|
+
const childSeen = new Set(seen).add(path);
|
|
152
|
+
const resolved = resolveRef(target, childSeen, ownerKey);
|
|
153
|
+
if (typeof resolved === "string" && resolved.includes("{")) return resolveInline(resolved, childSeen, ownerKey);
|
|
199
154
|
return String(resolved);
|
|
200
155
|
});
|
|
201
156
|
};
|
|
@@ -249,10 +204,4 @@ Object.defineProperty(exports, 'toFlatTokens', {
|
|
|
249
204
|
get: function () {
|
|
250
205
|
return toFlatTokens;
|
|
251
206
|
}
|
|
252
|
-
});
|
|
253
|
-
Object.defineProperty(exports, 'validateRefs', {
|
|
254
|
-
enumerable: true,
|
|
255
|
-
get: function () {
|
|
256
|
-
return validateRefs;
|
|
257
|
-
}
|
|
258
207
|
});
|