@motion-proto/live-tokens 0.71.0 → 0.72.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/.claude/skills/live-tokens-check-compliance/SKILL.md +7 -1
- package/.claude/skills/live-tokens-create-component/SKILL.md +2 -2
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +99 -0
- package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +1 -2
- package/.claude/skills/live-tokens-create-component/references/token-naming.md +6 -0
- package/CHANGELOG.md +77 -0
- package/README.md +1 -1
- package/bin/check-component.mjs +2 -11
- package/bin/check-page.mjs +3 -2
- package/bin/cli.mjs +2 -1
- package/bin/lib/catalogue.mjs +6 -2
- package/bin/lib/findings.mjs +17 -0
- package/bin/lib/report.mjs +1 -1
- package/bin/lib/tokenVocabulary.mjs +20 -0
- package/dist-plugin/adjust/index.cjs +33 -6
- package/dist-plugin/adjust/index.d.cts +2 -2
- package/dist-plugin/adjust/index.d.ts +2 -2
- package/dist-plugin/adjust/index.js +7 -6
- package/dist-plugin/{chunk-2YNERPXY.js → chunk-RIXO2E55.js} +1 -1
- package/dist-plugin/{chunk-RVE3MNKM.js → chunk-W6Y4BWFB.js} +3 -1
- package/dist-plugin/{chunk-GPIBU44G.js → chunk-YLCOIGQC.js} +25 -1
- package/dist-plugin/{dataPaths-bJTCEO4H.d.ts → dataPaths-BhWzd5cL.d.cts} +4 -0
- package/dist-plugin/{dataPaths-bJTCEO4H.d.cts → dataPaths-BhWzd5cL.d.ts} +4 -0
- package/dist-plugin/fontPairing/index.cjs +3 -1
- package/dist-plugin/fontPairing/index.d.cts +1 -1
- package/dist-plugin/fontPairing/index.d.ts +1 -1
- package/dist-plugin/fontPairing/index.js +1 -1
- package/dist-plugin/generateColorsAndType/index.cjs +28 -2
- package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
- package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
- package/dist-plugin/generateColorsAndType/index.js +2 -2
- package/dist-plugin/index.cjs +28 -2
- package/dist-plugin/index.d.cts +1 -1
- package/dist-plugin/index.d.ts +1 -1
- package/dist-plugin/index.js +3 -3
- package/dist-plugin/migrateData/index.cjs +28 -2
- package/dist-plugin/migrateData/index.d.cts +1 -1
- package/dist-plugin/migrateData/index.d.ts +1 -1
- package/dist-plugin/migrateData/index.js +3 -3
- package/dist-plugin/tokensCssMigrations/index.cjs +3 -1
- package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
- package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
- package/dist-plugin/tokensCssMigrations/index.js +1 -1
- package/package.json +5 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +1 -11
- package/src/editor/component-editor/contract.ts +175 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +0 -8
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +5 -0
- package/src/editor/component-editor/scaffolding/TypeEditor.svelte +0 -21
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +2 -2
- package/src/editor/component-editor/scaffolding/types.ts +1 -7
- package/src/editor/core/components/aliasKinds.ts +6 -2
- package/src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts +39 -0
- package/src/editor/core/themes/migrations/index.ts +2 -0
- package/src/live-tokens/data/themes/autumn.json +1 -7
- package/src/live-tokens/data/themes/halloween.json +1 -7
- package/src/live-tokens/data/themes/midnight-study.json +1 -7
- package/src/live-tokens/data/themes/ocean.json +1 -7
- package/src/live-tokens/data/themes/royal-velvet.json +1 -7
- package/src/live-tokens/data/themes/sketchy.json +1 -7
- package/src/live-tokens/data/themes/spring-meadow.json +1 -7
- package/src/live-tokens/data/themes/sunset.json +1 -7
- package/src/system/components/SectionDivider.svelte +3 -143
- package/src/system/components/FloatingTokenTags.css +0 -284
- package/src/system/components/FloatingTokenTags.svelte +0 -570
|
@@ -382,7 +382,9 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
382
382
|
"componentConfigsDir",
|
|
383
383
|
"themesDir",
|
|
384
384
|
"manifestsDir",
|
|
385
|
-
"tokensCssPath"
|
|
385
|
+
"tokensCssPath",
|
|
386
|
+
"componentDirs",
|
|
387
|
+
"checks"
|
|
386
388
|
]);
|
|
387
389
|
var cached = null;
|
|
388
390
|
function readLiveTokensConfig() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-DSV3Zisf.cjs';
|
|
2
|
-
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-
|
|
2
|
+
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.cjs';
|
|
3
3
|
|
|
4
4
|
type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
|
|
5
5
|
interface PairingFace {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-DSV3Zisf.js';
|
|
2
|
-
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-
|
|
2
|
+
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.js';
|
|
3
3
|
|
|
4
4
|
type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
|
|
5
5
|
interface PairingFace {
|
|
@@ -1651,6 +1651,29 @@ var componentMigration_2026_09_01_gateSuffixEnabled = {
|
|
|
1651
1651
|
}
|
|
1652
1652
|
};
|
|
1653
1653
|
|
|
1654
|
+
// src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts
|
|
1655
|
+
var DROPPED2 = new Set(
|
|
1656
|
+
["lg", "md", "sm"].flatMap((v) => [
|
|
1657
|
+
`--sectiondivider-${v}-title-outline-width`,
|
|
1658
|
+
`--sectiondivider-${v}-title-outline-color`
|
|
1659
|
+
])
|
|
1660
|
+
);
|
|
1661
|
+
var componentMigration_2026_09_02_sectiondividerDropTitleOutline = {
|
|
1662
|
+
id: "2026-09-02-sectiondivider-drop-title-outline",
|
|
1663
|
+
fromVersion: 25,
|
|
1664
|
+
toVersion: 26,
|
|
1665
|
+
appliesTo: "component-config",
|
|
1666
|
+
apply(rawVars, meta) {
|
|
1667
|
+
if (meta.component !== "sectiondivider") return { ...rawVars };
|
|
1668
|
+
const out = {};
|
|
1669
|
+
for (const [key, value] of Object.entries(rawVars)) {
|
|
1670
|
+
if (DROPPED2.has(key)) continue;
|
|
1671
|
+
out[key] = value;
|
|
1672
|
+
}
|
|
1673
|
+
return out;
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
|
|
1654
1677
|
// src/editor/core/themes/migrations/index.ts
|
|
1655
1678
|
var MIGRATIONS = [
|
|
1656
1679
|
colorsAndTypeMigration_2026_04_24_legacyKeysAndBgToCanvas,
|
|
@@ -1684,7 +1707,8 @@ var MIGRATIONS = [
|
|
|
1684
1707
|
colorsAndTypeMigration_2026_09_01_tintRename,
|
|
1685
1708
|
componentMigration_2026_09_01_tintRename,
|
|
1686
1709
|
componentMigration_2026_09_01_tabbarActiveTint,
|
|
1687
|
-
componentMigration_2026_09_01_gateSuffixEnabled
|
|
1710
|
+
componentMigration_2026_09_01_gateSuffixEnabled,
|
|
1711
|
+
componentMigration_2026_09_02_sectiondividerDropTitleOutline
|
|
1688
1712
|
];
|
|
1689
1713
|
function countFor(kind) {
|
|
1690
1714
|
return MIGRATIONS.filter((m) => m.appliesTo === kind).length;
|
|
@@ -1999,7 +2023,9 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1999
2023
|
"componentConfigsDir",
|
|
2000
2024
|
"themesDir",
|
|
2001
2025
|
"manifestsDir",
|
|
2002
|
-
"tokensCssPath"
|
|
2026
|
+
"tokensCssPath",
|
|
2027
|
+
"componentDirs",
|
|
2028
|
+
"checks"
|
|
2003
2029
|
]);
|
|
2004
2030
|
var cached = null;
|
|
2005
2031
|
function readLiveTokensConfig() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { F as FontSource, b as FontStack, G as GradientDiskToken, O as Oklch, H as HarmonyMode, C as ColorsAndType } from '../themeTypes-DSV3Zisf.cjs';
|
|
2
|
-
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-
|
|
2
|
+
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.cjs';
|
|
3
3
|
export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { F as FontSource, b as FontStack, G as GradientDiskToken, O as Oklch, H as HarmonyMode, C as ColorsAndType } from '../themeTypes-DSV3Zisf.js';
|
|
2
|
-
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-
|
|
2
|
+
export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.js';
|
|
3
3
|
export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
CURRENT_COLORS_AND_TYPE_SCHEMA_VERSION,
|
|
21
21
|
CURRENT_COMPONENT_SCHEMA_VERSION
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-YLCOIGQC.js";
|
|
23
23
|
import {
|
|
24
24
|
cssColorToOklch,
|
|
25
25
|
gamutClamp,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
import {
|
|
31
31
|
readLiveTokensConfig,
|
|
32
32
|
resolveDataDirs
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-W6Y4BWFB.js";
|
|
34
34
|
|
|
35
35
|
// src/editor/core/store/editorCore.ts
|
|
36
36
|
import { writable, derived, get } from "svelte/store";
|
package/dist-plugin/index.cjs
CHANGED
|
@@ -1587,6 +1587,29 @@ var componentMigration_2026_09_01_gateSuffixEnabled = {
|
|
|
1587
1587
|
}
|
|
1588
1588
|
};
|
|
1589
1589
|
|
|
1590
|
+
// src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts
|
|
1591
|
+
var DROPPED2 = new Set(
|
|
1592
|
+
["lg", "md", "sm"].flatMap((v) => [
|
|
1593
|
+
`--sectiondivider-${v}-title-outline-width`,
|
|
1594
|
+
`--sectiondivider-${v}-title-outline-color`
|
|
1595
|
+
])
|
|
1596
|
+
);
|
|
1597
|
+
var componentMigration_2026_09_02_sectiondividerDropTitleOutline = {
|
|
1598
|
+
id: "2026-09-02-sectiondivider-drop-title-outline",
|
|
1599
|
+
fromVersion: 25,
|
|
1600
|
+
toVersion: 26,
|
|
1601
|
+
appliesTo: "component-config",
|
|
1602
|
+
apply(rawVars, meta) {
|
|
1603
|
+
if (meta.component !== "sectiondivider") return { ...rawVars };
|
|
1604
|
+
const out = {};
|
|
1605
|
+
for (const [key, value] of Object.entries(rawVars)) {
|
|
1606
|
+
if (DROPPED2.has(key)) continue;
|
|
1607
|
+
out[key] = value;
|
|
1608
|
+
}
|
|
1609
|
+
return out;
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
|
|
1590
1613
|
// src/editor/core/themes/migrations/index.ts
|
|
1591
1614
|
var MIGRATIONS = [
|
|
1592
1615
|
colorsAndTypeMigration_2026_04_24_legacyKeysAndBgToCanvas,
|
|
@@ -1620,7 +1643,8 @@ var MIGRATIONS = [
|
|
|
1620
1643
|
colorsAndTypeMigration_2026_09_01_tintRename,
|
|
1621
1644
|
componentMigration_2026_09_01_tintRename,
|
|
1622
1645
|
componentMigration_2026_09_01_tabbarActiveTint,
|
|
1623
|
-
componentMigration_2026_09_01_gateSuffixEnabled
|
|
1646
|
+
componentMigration_2026_09_01_gateSuffixEnabled,
|
|
1647
|
+
componentMigration_2026_09_02_sectiondividerDropTitleOutline
|
|
1624
1648
|
];
|
|
1625
1649
|
function countFor(kind) {
|
|
1626
1650
|
return MIGRATIONS.filter((m) => m.appliesTo === kind).length;
|
|
@@ -2601,7 +2625,9 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
2601
2625
|
"componentConfigsDir",
|
|
2602
2626
|
"themesDir",
|
|
2603
2627
|
"manifestsDir",
|
|
2604
|
-
"tokensCssPath"
|
|
2628
|
+
"tokensCssPath",
|
|
2629
|
+
"componentDirs",
|
|
2630
|
+
"checks"
|
|
2605
2631
|
]);
|
|
2606
2632
|
var cached = null;
|
|
2607
2633
|
function readLiveTokensConfig() {
|
package/dist-plugin/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
export { ComponentSource, MissingToken, RunResult, TOKENS_CSS_MIGRATIONS, TokensCssMigration, ValidateInput, runTokensCssMigrations, validateTokensCss } from './tokensCssMigrations/index.cjs';
|
|
3
|
-
import './dataPaths-
|
|
3
|
+
import './dataPaths-BhWzd5cL.cjs';
|
|
4
4
|
|
|
5
5
|
interface ThemeFileApiOptions {
|
|
6
6
|
tokensCssPath: string;
|
package/dist-plugin/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
export { ComponentSource, MissingToken, RunResult, TOKENS_CSS_MIGRATIONS, TokensCssMigration, ValidateInput, runTokensCssMigrations, validateTokensCss } from './tokensCssMigrations/index.js';
|
|
3
|
-
import './dataPaths-
|
|
3
|
+
import './dataPaths-BhWzd5cL.js';
|
|
4
4
|
|
|
5
5
|
interface ThemeFileApiOptions {
|
|
6
6
|
tokensCssPath: string;
|
package/dist-plugin/index.js
CHANGED
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
nextAvailableName,
|
|
14
14
|
normalizeTheme,
|
|
15
15
|
versionedFileResourceServer
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-RIXO2E55.js";
|
|
17
17
|
import {
|
|
18
18
|
CURRENT_COMPONENT_SCHEMA_VERSION
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-YLCOIGQC.js";
|
|
20
20
|
import {
|
|
21
21
|
TOKENS_CSS_MIGRATIONS,
|
|
22
22
|
extractGlobalRootBody,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
} from "./chunk-7TQQTHI6.js";
|
|
32
32
|
import {
|
|
33
33
|
resolveDataDirs
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-W6Y4BWFB.js";
|
|
35
35
|
|
|
36
36
|
// vite-plugin/themeFileApi.ts
|
|
37
37
|
import fs from "fs";
|
|
@@ -976,6 +976,29 @@ var componentMigration_2026_09_01_gateSuffixEnabled = {
|
|
|
976
976
|
}
|
|
977
977
|
};
|
|
978
978
|
|
|
979
|
+
// src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts
|
|
980
|
+
var DROPPED2 = new Set(
|
|
981
|
+
["lg", "md", "sm"].flatMap((v) => [
|
|
982
|
+
`--sectiondivider-${v}-title-outline-width`,
|
|
983
|
+
`--sectiondivider-${v}-title-outline-color`
|
|
984
|
+
])
|
|
985
|
+
);
|
|
986
|
+
var componentMigration_2026_09_02_sectiondividerDropTitleOutline = {
|
|
987
|
+
id: "2026-09-02-sectiondivider-drop-title-outline",
|
|
988
|
+
fromVersion: 25,
|
|
989
|
+
toVersion: 26,
|
|
990
|
+
appliesTo: "component-config",
|
|
991
|
+
apply(rawVars, meta) {
|
|
992
|
+
if (meta.component !== "sectiondivider") return { ...rawVars };
|
|
993
|
+
const out = {};
|
|
994
|
+
for (const [key, value] of Object.entries(rawVars)) {
|
|
995
|
+
if (DROPPED2.has(key)) continue;
|
|
996
|
+
out[key] = value;
|
|
997
|
+
}
|
|
998
|
+
return out;
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
|
|
979
1002
|
// src/editor/core/themes/migrations/index.ts
|
|
980
1003
|
var MIGRATIONS = [
|
|
981
1004
|
colorsAndTypeMigration_2026_04_24_legacyKeysAndBgToCanvas,
|
|
@@ -1009,7 +1032,8 @@ var MIGRATIONS = [
|
|
|
1009
1032
|
colorsAndTypeMigration_2026_09_01_tintRename,
|
|
1010
1033
|
componentMigration_2026_09_01_tintRename,
|
|
1011
1034
|
componentMigration_2026_09_01_tabbarActiveTint,
|
|
1012
|
-
componentMigration_2026_09_01_gateSuffixEnabled
|
|
1035
|
+
componentMigration_2026_09_01_gateSuffixEnabled,
|
|
1036
|
+
componentMigration_2026_09_02_sectiondividerDropTitleOutline
|
|
1013
1037
|
];
|
|
1014
1038
|
function countFor(kind) {
|
|
1015
1039
|
return MIGRATIONS.filter((m) => m.appliesTo === kind).length;
|
|
@@ -2178,7 +2202,9 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
2178
2202
|
"componentConfigsDir",
|
|
2179
2203
|
"themesDir",
|
|
2180
2204
|
"manifestsDir",
|
|
2181
|
-
"tokensCssPath"
|
|
2205
|
+
"tokensCssPath",
|
|
2206
|
+
"componentDirs",
|
|
2207
|
+
"checks"
|
|
2182
2208
|
]);
|
|
2183
2209
|
var cached = null;
|
|
2184
2210
|
function readLiveTokensConfig() {
|
|
@@ -7,14 +7,14 @@ import {
|
|
|
7
7
|
normalizeTheme,
|
|
8
8
|
planLegacyRenames,
|
|
9
9
|
versionedFileResourceServer
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-RIXO2E55.js";
|
|
11
11
|
import {
|
|
12
12
|
CURRENT_COMPONENT_SCHEMA_VERSION
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-YLCOIGQC.js";
|
|
14
14
|
import {
|
|
15
15
|
readLiveTokensConfig,
|
|
16
16
|
resolveDataDirs
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-W6Y4BWFB.js";
|
|
18
18
|
|
|
19
19
|
// vite-plugin/migrateData/migrateData.ts
|
|
20
20
|
import fs from "fs";
|
|
@@ -648,7 +648,9 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
648
648
|
"componentConfigsDir",
|
|
649
649
|
"themesDir",
|
|
650
650
|
"manifestsDir",
|
|
651
|
-
"tokensCssPath"
|
|
651
|
+
"tokensCssPath",
|
|
652
|
+
"componentDirs",
|
|
653
|
+
"checks"
|
|
652
654
|
]);
|
|
653
655
|
var cached = null;
|
|
654
656
|
function readLiveTokensConfig() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motion-proto/live-tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
|
|
6
6
|
"keywords": [
|
|
@@ -72,6 +72,10 @@
|
|
|
72
72
|
"types": "./src/editor/component-editor/index.ts",
|
|
73
73
|
"default": "./src/editor/component-editor/index.ts"
|
|
74
74
|
},
|
|
75
|
+
"./component-editor/contract": {
|
|
76
|
+
"types": "./src/editor/component-editor/contract.ts",
|
|
77
|
+
"default": "./src/editor/component-editor/contract.ts"
|
|
78
|
+
},
|
|
75
79
|
"./ui": {
|
|
76
80
|
"svelte": "./src/editor/ui/index.ts",
|
|
77
81
|
"types": "./src/editor/ui/index.ts",
|
|
@@ -36,25 +36,18 @@
|
|
|
36
36
|
{ label: 'hairline thickness', canBeLinked: true, groupKey: 'hairline-thickness', variable: `--sectiondivider-${v}-hairline-thickness`, element: 'hairline' },
|
|
37
37
|
];
|
|
38
38
|
}
|
|
39
|
-
function titleOutlineTokens(v: Variant): Token[] {
|
|
40
|
-
return [
|
|
41
|
-
{ label: 'outline thickness', canBeLinked: true, groupKey: 'title-outline-width', variable: `--sectiondivider-${v}-title-outline-width`, element: 'title' },
|
|
42
|
-
{ label: 'outline color', canBeLinked: true, groupKey: 'title-outline-color', variable: `--sectiondivider-${v}-title-outline-color`, element: 'title' },
|
|
43
|
-
];
|
|
44
|
-
}
|
|
45
39
|
function backgroundTokens(v: Variant): Token[] {
|
|
46
40
|
return [
|
|
47
41
|
{ label: 'background', groupKey: 'background', variable: `--sectiondivider-${v}-background`, kind: 'gradient', family: variants.find((x) => x.key === v)!.family },
|
|
48
42
|
];
|
|
49
43
|
}
|
|
50
44
|
function variantTokens(v: Variant): Token[] {
|
|
51
|
-
return [...containerTokens(v), ...hairlineTokens(v), ...
|
|
45
|
+
return [...containerTokens(v), ...hairlineTokens(v), ...backgroundTokens(v), ...typePaddingTokens(v)];
|
|
52
46
|
}
|
|
53
47
|
function stateTokens(v: Variant): Token[] {
|
|
54
48
|
return [
|
|
55
49
|
...containerTokens(v),
|
|
56
50
|
...hairlineTokens(v),
|
|
57
|
-
...titleOutlineTokens(v).map((t) => ({ ...t, hidden: true })),
|
|
58
51
|
...backgroundTokens(v).map((t) => ({ ...t, hidden: true })),
|
|
59
52
|
...typePaddingTokens(v),
|
|
60
53
|
];
|
|
@@ -72,8 +65,6 @@
|
|
|
72
65
|
weightVariable: `--sectiondivider-${v}-title-font-weight`,
|
|
73
66
|
lineHeightVariable: `--sectiondivider-${v}-title-line-height`,
|
|
74
67
|
letterSpacingVariable: `--sectiondivider-${v}-title-letter-spacing`,
|
|
75
|
-
outlineWidthVariable: `--sectiondivider-${v}-title-outline-width`,
|
|
76
|
-
outlineColorVariable: `--sectiondivider-${v}-title-outline-color`,
|
|
77
68
|
},
|
|
78
69
|
{
|
|
79
70
|
legend: '',
|
|
@@ -127,7 +118,6 @@
|
|
|
127
118
|
const LINKED_GROUP_KEYS = [
|
|
128
119
|
'container-padding', 'radius', 'border', 'border-width', 'shadow',
|
|
129
120
|
'hairline-color', 'hairline-thickness',
|
|
130
|
-
'title-outline-width', 'title-outline-color',
|
|
131
121
|
'title-color', 'description-color', 'eyebrow-color',
|
|
132
122
|
'title-padding', 'description-padding', 'eyebrow-padding',
|
|
133
123
|
'title-font-family', 'title-font-weight', 'title-line-height', 'title-letter-spacing',
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry contract, as a function a project outside this package can run.
|
|
3
|
+
*
|
|
4
|
+
* Node-only: it reads the runtime `.svelte` file and the component's
|
|
5
|
+
* `default.json` off disk, so it lives behind its own subpath export
|
|
6
|
+
* (`@motion-proto/live-tokens/component-editor/contract`) rather than in the
|
|
7
|
+
* browser barrel. A consumer's whole suite is a `describe.each` over their own
|
|
8
|
+
* registrations and one call:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* // @vitest-environment happy-dom
|
|
12
|
+
* import { describe, it, expect } from 'vitest';
|
|
13
|
+
* import { getComponentRegistryEntries, registerComponent } from '@motion-proto/live-tokens';
|
|
14
|
+
* import { checkRegistryEntry } from '@motion-proto/live-tokens/component-editor/contract';
|
|
15
|
+
* import MyWidgetEditor, { allTokens } from '../src/system/components/MyWidgetEditor.svelte';
|
|
16
|
+
*
|
|
17
|
+
* registerComponent({ id: 'mywidget', label: 'My Widget', icon: 'fas fa-magic',
|
|
18
|
+
* sourceFile: 'src/system/components/MyWidget.svelte',
|
|
19
|
+
* editorComponent: MyWidgetEditor, schema: allTokens });
|
|
20
|
+
*
|
|
21
|
+
* const mine = getComponentRegistryEntries().filter((e) => e.origin === 'custom');
|
|
22
|
+
*
|
|
23
|
+
* describe.each(mine.map((e) => [e.id, e] as const))('%s', (_id, entry) => {
|
|
24
|
+
* it('meets the registry contract', () => {
|
|
25
|
+
* expect(checkRegistryEntry(entry)).toEqual([]);
|
|
26
|
+
* });
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* The `origin` filter is load-bearing outside this package: the registry always
|
|
31
|
+
* carries the shipped components too, and their `sourceFile` paths are relative
|
|
32
|
+
* to the package root, not the consumer's.
|
|
33
|
+
*/
|
|
34
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
35
|
+
import path from 'node:path';
|
|
36
|
+
import { get } from 'svelte/store';
|
|
37
|
+
import type { RegistryEntry } from './registry';
|
|
38
|
+
import type { Token } from './scaffolding/types';
|
|
39
|
+
import { parseColorOpacity } from '../core/themes/parsers/colorOpacity';
|
|
40
|
+
import { editorState, setComponentAlias, clearComponentAlias } from '../core/store/editorStore';
|
|
41
|
+
|
|
42
|
+
export interface RegistryContractOptions {
|
|
43
|
+
/** Root that `entry.sourceFile` is relative to. Defaults to `process.cwd()`,
|
|
44
|
+
which is the project root under both vitest and `npm test`. */
|
|
45
|
+
projectRoot?: string;
|
|
46
|
+
/** Directory holding `<id>/default.json`. Defaults to
|
|
47
|
+
`<projectRoot>/src/live-tokens/data/component-configs`. Pass the
|
|
48
|
+
`componentConfigsDir` from `live-tokens.config.json` when a project moved it. */
|
|
49
|
+
componentConfigsDir?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const PADDING_SIDE_RE = /-padding-(top|right|bottom|left)$/;
|
|
53
|
+
const PROBE = { kind: 'token', name: '--space-8' } as const;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Tokens excluded from the runtime-declaration and default-seed checks:
|
|
57
|
+
* - `hidden: true` (vestigial or hidden by design),
|
|
58
|
+
* - `kind: 'gradient'` (structured payloads, stored as gradient objects, not vars),
|
|
59
|
+
* - `-padding-(top|right|bottom|left)` (written on demand by the split-padding
|
|
60
|
+
* UI and consumed through the `themed-padding` mixin's fallback chain, so
|
|
61
|
+
* they exist as neither `:root` declarations nor default seeds).
|
|
62
|
+
*/
|
|
63
|
+
function isEditableSurfaceToken(t: Token): boolean {
|
|
64
|
+
if (t.hidden) return false;
|
|
65
|
+
if (t.kind === 'gradient') return false;
|
|
66
|
+
if (PADDING_SIDE_RE.test(t.variable)) return false;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Every `--xxx:` declaration in every `<style>` block of a Svelte file. */
|
|
71
|
+
function extractRuntimeDeclarations(source: string): Set<string> {
|
|
72
|
+
const out = new Set<string>();
|
|
73
|
+
const blocks = [...source.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)].map((m) => m[1]);
|
|
74
|
+
for (const block of blocks) {
|
|
75
|
+
for (const m of block.matchAll(/(?:^|[{;\n])\s*(--[a-z0-9-]+)\s*:/g)) {
|
|
76
|
+
out.add(m[1]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Opacity a seeded alias resolves to. A plain token alias is fully opaque; a
|
|
83
|
+
gradient payload or `transparent` carries no guarantee, so it reads as 0. */
|
|
84
|
+
function seededOpacity(raw: unknown): number {
|
|
85
|
+
if (typeof raw !== 'string' || raw === 'transparent') return 0;
|
|
86
|
+
return parseColorOpacity(raw)?.opacity ?? 100;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Run the registry contract against one entry and return a violation line per
|
|
91
|
+
* failure. An empty array is the pass.
|
|
92
|
+
*
|
|
93
|
+
* The contract:
|
|
94
|
+
* 1. Registration — `sourceFile` resolves to a real file, the schema is non-empty.
|
|
95
|
+
* 2. Uniqueness — no schema variable is declared twice.
|
|
96
|
+
* 3. Editor ↔ runtime — every editable token's CSS var is declared in the
|
|
97
|
+
* runtime's `<style>` block, so an edit has something to repaint.
|
|
98
|
+
* 4. Editor ↔ default config — every editable token has a seed alias in
|
|
99
|
+
* `<id>/default.json`, so a consumer adopts with full defaults. A component
|
|
100
|
+
* with no `default.json` is editor-only; the seed and floor checks skip it.
|
|
101
|
+
* 5. Opacity floors — a token declaring `minOpacity` ships a default at or
|
|
102
|
+
* above it, so a floating panel starts out legible over page content.
|
|
103
|
+
* 6. Round-trip — `setComponentAlias` persists into the slice under the same key.
|
|
104
|
+
*/
|
|
105
|
+
export function checkRegistryEntry(
|
|
106
|
+
entry: RegistryEntry,
|
|
107
|
+
options: RegistryContractOptions = {},
|
|
108
|
+
): string[] {
|
|
109
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
110
|
+
const configsDir =
|
|
111
|
+
options.componentConfigsDir ??
|
|
112
|
+
path.resolve(projectRoot, 'src/live-tokens/data/component-configs');
|
|
113
|
+
|
|
114
|
+
const violations: string[] = [];
|
|
115
|
+
const schema = entry.schema ?? [];
|
|
116
|
+
const runtimePath = path.resolve(projectRoot, entry.sourceFile);
|
|
117
|
+
|
|
118
|
+
if (schema.length === 0) violations.push('registration: schema is empty');
|
|
119
|
+
if (!existsSync(runtimePath)) {
|
|
120
|
+
violations.push(`registration: sourceFile does not resolve to a file (${runtimePath})`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const seen = new Set<string>();
|
|
124
|
+
for (const t of schema) {
|
|
125
|
+
if (seen.has(t.variable)) violations.push(`uniqueness: ${t.variable} is declared twice`);
|
|
126
|
+
seen.add(t.variable);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const editable = schema.filter(isEditableSurfaceToken);
|
|
130
|
+
|
|
131
|
+
if (existsSync(runtimePath)) {
|
|
132
|
+
const runtimeVars = extractRuntimeDeclarations(readFileSync(runtimePath, 'utf-8'));
|
|
133
|
+
for (const t of editable) {
|
|
134
|
+
if (!runtimeVars.has(t.variable)) {
|
|
135
|
+
violations.push(`runtime: ${t.variable} is not declared in ${entry.sourceFile}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const configPath = path.join(configsDir, entry.id, 'default.json');
|
|
141
|
+
if (existsSync(configPath)) {
|
|
142
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8')) as {
|
|
143
|
+
aliases?: Record<string, unknown>;
|
|
144
|
+
};
|
|
145
|
+
const aliases = config.aliases ?? {};
|
|
146
|
+
for (const t of editable) {
|
|
147
|
+
if (!(t.variable in aliases)) {
|
|
148
|
+
violations.push(`default config: ${t.variable} has no seed in ${entry.id}/default.json`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
for (const t of schema) {
|
|
152
|
+
if (t.minOpacity === undefined) continue;
|
|
153
|
+
const opacity = seededOpacity(aliases[t.variable]);
|
|
154
|
+
if (opacity < t.minOpacity) {
|
|
155
|
+
violations.push(
|
|
156
|
+
`opacity floor: ${t.variable} seeds at ${opacity}%, below its ${t.minOpacity}% floor`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const sample = editable[0];
|
|
163
|
+
if (sample) {
|
|
164
|
+
setComponentAlias(entry.id, sample.variable, PROBE);
|
|
165
|
+
const stored = get(editorState).components[entry.id]?.aliases?.[sample.variable];
|
|
166
|
+
if (JSON.stringify(stored) !== JSON.stringify(PROBE)) {
|
|
167
|
+
violations.push(
|
|
168
|
+
`round-trip: setComponentAlias(${entry.id}, ${sample.variable}) stored ${JSON.stringify(stored)}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
clearComponentAlias(entry.id, sample.variable);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return violations;
|
|
175
|
+
}
|
|
@@ -140,10 +140,6 @@
|
|
|
140
140
|
lineHeightLabel={tg.lineHeightLabel ?? 'line height'}
|
|
141
141
|
letterSpacingVariable={tg.letterSpacingVariable}
|
|
142
142
|
letterSpacingLabel={tg.letterSpacingLabel ?? 'letter spacing'}
|
|
143
|
-
outlineWidthVariable={tg.outlineWidthVariable}
|
|
144
|
-
outlineWidthLabel={tg.outlineWidthLabel ?? 'outline thickness'}
|
|
145
|
-
outlineColorVariable={tg.outlineColorVariable}
|
|
146
|
-
outlineColorLabel={tg.outlineColorLabel ?? 'outline color'}
|
|
147
143
|
{component}
|
|
148
144
|
{onchange}
|
|
149
145
|
/>
|
|
@@ -183,10 +179,6 @@
|
|
|
183
179
|
lineHeightLabel={tg.lineHeightLabel ?? 'line height'}
|
|
184
180
|
letterSpacingVariable={tg.letterSpacingVariable}
|
|
185
181
|
letterSpacingLabel={tg.letterSpacingLabel ?? 'letter spacing'}
|
|
186
|
-
outlineWidthVariable={tg.outlineWidthVariable}
|
|
187
|
-
outlineWidthLabel={tg.outlineWidthLabel ?? 'outline thickness'}
|
|
188
|
-
outlineColorVariable={tg.outlineColorVariable}
|
|
189
|
-
outlineColorLabel={tg.outlineColorLabel ?? 'outline color'}
|
|
190
182
|
{component}
|
|
191
183
|
{onchange}
|
|
192
184
|
/>
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
'divider-height',
|
|
73
73
|
'divider-inset',
|
|
74
74
|
'dot-size',
|
|
75
|
+
'length',
|
|
75
76
|
'radius',
|
|
76
77
|
'padding',
|
|
77
78
|
'padding-split',
|
|
@@ -150,6 +151,9 @@
|
|
|
150
151
|
extra: () => ({ mode: 'sides' }),
|
|
151
152
|
},
|
|
152
153
|
'gap': { component: UIPaddingSelector, extra: () => ({ mode: 'single', splittable: false }) },
|
|
154
|
+
/* A bare width/height/size is a length off the same `--space-*` scale gap
|
|
155
|
+
reads from, so it takes gap's picker rather than a scale of its own. */
|
|
156
|
+
'length': { component: UIPaddingSelector, extra: () => ({ mode: 'single', splittable: false }) },
|
|
153
157
|
'duration': { component: UIVariantSelector, extra: () => ({ ...DURATION }) },
|
|
154
158
|
'easing': { component: UIEasingSelector },
|
|
155
159
|
'blur': { component: UIVariantSelector, extra: () => ({ ...BLUR }) },
|
|
@@ -176,6 +180,7 @@
|
|
|
176
180
|
'divider-height',
|
|
177
181
|
'divider-inset',
|
|
178
182
|
'dot-size',
|
|
183
|
+
'length',
|
|
179
184
|
'radius',
|
|
180
185
|
'padding',
|
|
181
186
|
'padding-split',
|