@motion-proto/live-tokens 0.50.0 → 0.53.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-adjust-shape-space/SKILL.md +7 -1
- package/CHANGELOG.md +108 -0
- package/bin/adjust.mjs +16 -39
- package/bin/generate-theme.mjs +20 -6
- package/dist-plugin/adjust/index.cjs +781 -21
- package/dist-plugin/adjust/index.d.cts +2 -1
- package/dist-plugin/adjust/index.d.ts +2 -1
- package/dist-plugin/adjust/index.js +43 -21
- package/dist-plugin/{chunk-6OZFXIQI.js → chunk-232GZGQU.js} +207 -12
- package/dist-plugin/chunk-NE6N66EE.js +753 -0
- package/dist-plugin/{chunk-76TFDTJO.js → chunk-PB2JTK2H.js} +179 -128
- package/dist-plugin/{chunk-44RSTAII.js → chunk-TKZBVIW5.js} +33 -5
- package/dist-plugin/chunk-YR6GPXW2.js +129 -0
- package/dist-plugin/generateColorsAndType/index.cjs +135 -63
- package/dist-plugin/generateColorsAndType/index.d.cts +2 -1
- package/dist-plugin/generateColorsAndType/index.d.ts +2 -1
- package/dist-plugin/generateColorsAndType/index.js +28 -784
- package/dist-plugin/{themeTypes-DSwKq-bj.d.ts → index-DpTIRZ2H.d.cts} +67 -26
- package/dist-plugin/{themeTypes-DSwKq-bj.d.cts → index-DpTIRZ2H.d.ts} +67 -26
- package/dist-plugin/index.cjs +1254 -140
- package/dist-plugin/index.js +106 -82
- package/dist-plugin/migrateData/index.cjs +956 -14
- package/dist-plugin/migrateData/index.js +10 -3
- package/dist-plugin/tokensCssMigrations/index.cjs +71 -5
- package/dist-plugin/tokensCssMigrations/index.d.cts +20 -9
- package/dist-plugin/tokensCssMigrations/index.d.ts +20 -9
- package/dist-plugin/tokensCssMigrations/index.js +4 -1
- package/package.json +8 -4
- package/src/editor/component-editor/NotificationEditor.svelte +6 -0
- package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +9 -23
- package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +8 -8
- package/src/editor/core/components/adjustAliases.ts +49 -22
- package/src/editor/core/components/aliasKinds.ts +7 -2
- package/src/editor/core/components/componentConfigService.ts +1 -1
- package/src/editor/core/palettes/oklch.ts +3 -137
- package/src/editor/core/palettes/paletteDerivation.ts +79 -37
- package/src/editor/core/store/editorStore.ts +7 -197
- package/src/editor/core/store/editorTypes.ts +9 -30
- package/src/editor/core/themes/generateColorsAndType.ts +18 -8
- package/src/editor/core/themes/lookSummary.ts +3 -25
- package/src/editor/core/themes/migrateComponentConfig.ts +205 -0
- package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +1 -1
- package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +1 -1
- package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +1 -1
- package/src/editor/core/themes/parsers/gradient.ts +211 -0
- package/src/editor/core/themes/slices/gradients.ts +5 -58
- package/src/editor/core/themes/themeService.ts +22 -9
- package/src/editor/core/themes/themeTypes.ts +89 -27
- package/src/editor/docs/content/editing-tokens.md +6 -2
- package/src/editor/docs/content/themes-workflow.md +9 -11
- package/src/editor/docs/content/where-themes-live.md +2 -2
- package/src/editor/docs/content.generated.ts +3 -3
- package/src/editor/index.ts +1 -0
- package/src/editor/styles/ui-editor.css +5 -2
- package/src/editor/ui/BezierCurveEditor.svelte +90 -67
- package/src/editor/ui/PaletteEditor.svelte +180 -59
- package/src/editor/ui/ThemePanel.svelte +24 -15
- package/src/editor/ui/Toggle.svelte +36 -24
- package/src/editor/ui/UIPillButton.svelte +1 -1
- package/src/editor/ui/UIReveal.svelte +47 -0
- package/src/editor/ui/UISquareButton.svelte +1 -1
- package/src/editor/ui/colors/ColorStory.svelte +12 -8
- package/src/editor/ui/colors/ColorsTab.svelte +4 -5
- package/src/editor/ui/curveEngine.ts +101 -21
- package/src/editor/ui/palette/OverridesPanel.svelte +23 -8
- package/src/editor/ui/palette/PaletteBase.svelte +17 -78
- package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
- package/src/editor/ui/palette/ScaleCurveEditor.svelte +110 -20
- package/src/editor/ui/palette/curveSummary.ts +20 -0
- package/src/editor/ui/palette/dockMagnify.ts +5 -13
- package/src/editor/ui/palette/paletteMath.ts +7 -4
- package/src/live-tokens/data/colors-and-type/autumn.json +528 -528
- package/src/live-tokens/data/colors-and-type/default.json +247 -247
- package/src/live-tokens/data/colors-and-type/halloween.json +532 -532
- package/src/live-tokens/data/colors-and-type/midnight-study.json +538 -538
- package/src/live-tokens/data/colors-and-type/ocean.json +521 -521
- package/src/live-tokens/data/colors-and-type/royal-velvet.json +532 -532
- package/src/live-tokens/data/colors-and-type/spring-meadow.json +504 -504
- package/src/live-tokens/data/colors-and-type/sunset.json +531 -531
- package/src/live-tokens/data/themes/autumn.json +604 -568
- package/src/live-tokens/data/themes/halloween.json +584 -583
- package/src/live-tokens/data/themes/midnight-study.json +673 -605
- package/src/live-tokens/data/themes/ocean.json +597 -561
- package/src/live-tokens/data/themes/royal-velvet.json +574 -573
- package/src/live-tokens/data/themes/spring-meadow.json +590 -556
- package/src/live-tokens/data/themes/sunset.json +632 -571
- package/src/live-tokens/data/tokens.generated.css +282 -282
- package/src/system/components/FloatingTokenTags.css +0 -1
- package/src/system/internal/backgroundContrast.ts +7 -0
- package/src/system/internal/oklch.ts +189 -0
- package/src/system/styles/tokens.css +283 -283
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// OKLCH color space conversions — hand-rolled, no dependencies
|
|
2
|
+
// Pipeline: sRGB hex → linear RGB → OKLab (via M1×M2 + cube root) → OKLCH (polar)
|
|
3
|
+
|
|
4
|
+
export interface Oklch {
|
|
5
|
+
l: number; // 0..1
|
|
6
|
+
c: number; // 0..~0.4
|
|
7
|
+
h: number; // 0..360
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// --- sRGB gamma ---
|
|
11
|
+
|
|
12
|
+
function srgbToLinear(c: number): number {
|
|
13
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function linearToSrgb(c: number): number {
|
|
17
|
+
return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// --- Hex ↔ linear RGB ---
|
|
21
|
+
|
|
22
|
+
function hexToLinearRgb(hex: string): [number, number, number] {
|
|
23
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
24
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
25
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
26
|
+
return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function linearRgbToHex(r: number, g: number, b: number): string {
|
|
30
|
+
const toHex = (c: number) => {
|
|
31
|
+
const v = Math.round(Math.max(0, Math.min(1, linearToSrgb(c))) * 255);
|
|
32
|
+
return v.toString(16).padStart(2, '0');
|
|
33
|
+
};
|
|
34
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- Linear RGB ↔ OKLab ---
|
|
38
|
+
// Uses the two-matrix approach from Björn Ottosson's blog
|
|
39
|
+
|
|
40
|
+
function linearRgbToOklab(r: number, g: number, b: number): [number, number, number] {
|
|
41
|
+
const l_ = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
|
42
|
+
const m_ = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
|
|
43
|
+
const s_ = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
|
|
44
|
+
|
|
45
|
+
const l = Math.cbrt(l_);
|
|
46
|
+
const m = Math.cbrt(m_);
|
|
47
|
+
const s = Math.cbrt(s_);
|
|
48
|
+
|
|
49
|
+
return [
|
|
50
|
+
0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
|
51
|
+
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
|
52
|
+
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function oklabToLinearRgb(L: number, a: number, b: number): [number, number, number] {
|
|
57
|
+
const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
|
|
58
|
+
const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
|
|
59
|
+
const s_ = L - 0.0894841775 * a - 1.2914855480 * b;
|
|
60
|
+
|
|
61
|
+
const l = l_ * l_ * l_;
|
|
62
|
+
const m = m_ * m_ * m_;
|
|
63
|
+
const s = s_ * s_ * s_;
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
|
67
|
+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
|
68
|
+
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- OKLab ↔ OKLCH ---
|
|
73
|
+
|
|
74
|
+
function oklabToOklch(L: number, a: number, b: number): Oklch {
|
|
75
|
+
const c = Math.sqrt(a * a + b * b);
|
|
76
|
+
let h = (Math.atan2(b, a) * 180) / Math.PI;
|
|
77
|
+
if (h < 0) h += 360;
|
|
78
|
+
return { l: L, c, h };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function oklchToOklab(l: number, c: number, h: number): [number, number, number] {
|
|
82
|
+
const hRad = (h * Math.PI) / 180;
|
|
83
|
+
return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// --- Public API ---
|
|
87
|
+
|
|
88
|
+
export function hexToOklch(hex: string): Oklch {
|
|
89
|
+
const [r, g, b] = hexToLinearRgb(hex);
|
|
90
|
+
const [L, a, bVal] = linearRgbToOklab(r, g, b);
|
|
91
|
+
return oklabToOklch(L, a, bVal);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function oklchToHex(l: number, c: number, h: number): string {
|
|
95
|
+
const [L, a, b] = oklchToOklab(l, c, h);
|
|
96
|
+
const [r, g, bVal] = oklabToLinearRgb(L, a, b);
|
|
97
|
+
return linearRgbToHex(r, g, bVal);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isInGamut(r: number, g: number, b: number): boolean {
|
|
101
|
+
const eps = 0.0001;
|
|
102
|
+
return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The clamped sRGB projection: reduce chroma into gamut, then serialize to hex.
|
|
106
|
+
* This is the correct projection for unclamped stored intent — naive
|
|
107
|
+
* `oklchToHex` per-channel clips (shifting hue), this preserves hue and L. */
|
|
108
|
+
export function oklchToHexClamped(l: number, c: number, h: number): string {
|
|
109
|
+
const g = gamutClamp(l, c, h);
|
|
110
|
+
return oklchToHex(g.l, g.c, g.h);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function gamutClamp(l: number, c: number, h: number): Oklch {
|
|
114
|
+
// Clamp lightness
|
|
115
|
+
if (l <= 0) return { l: 0, c: 0, h };
|
|
116
|
+
if (l >= 1) return { l: 1, c: 0, h };
|
|
117
|
+
|
|
118
|
+
// Binary search: reduce chroma until in sRGB gamut
|
|
119
|
+
const [L, a, b] = oklchToOklab(l, c, h);
|
|
120
|
+
const [r, g, bVal] = oklabToLinearRgb(L, a, b);
|
|
121
|
+
|
|
122
|
+
if (isInGamut(r, g, bVal)) return { l, c, h };
|
|
123
|
+
|
|
124
|
+
let lo = 0;
|
|
125
|
+
let hi = c;
|
|
126
|
+
for (let i = 0; i < 20; i++) {
|
|
127
|
+
const mid = (lo + hi) / 2;
|
|
128
|
+
const [La, aa, ba] = oklchToOklab(l, mid, h);
|
|
129
|
+
const [rr, gg, bb] = oklabToLinearRgb(La, aa, ba);
|
|
130
|
+
if (isInGamut(rr, gg, bb)) {
|
|
131
|
+
lo = mid;
|
|
132
|
+
} else {
|
|
133
|
+
hi = mid;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { l, c: lo, h };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// --- CSS serialization + parsing ---
|
|
140
|
+
|
|
141
|
+
function trim(value: number, decimals: number): string {
|
|
142
|
+
return Number(value.toFixed(decimals)).toString();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function oklchToCss(l: number, c: number, h: number): string {
|
|
146
|
+
const cs = trim(c, 4);
|
|
147
|
+
// Hue is meaningless without chroma, and the OKLab round trip leaves noise
|
|
148
|
+
// there (#ffffff lands on h≈89.88). Pin it so achromatic values read as
|
|
149
|
+
// achromatic instead of arbitrary.
|
|
150
|
+
return `oklch(${trim(l, 4)} ${cs} ${cs === '0' ? '0' : trim(h, 2)})`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The clamped sRGB projection as an `oklch()` string — the CSS counterpart of
|
|
154
|
+
* `oklchToHexClamped`. Clamping keeps flip-day output visually identical to
|
|
155
|
+
* the hex it replaces; emitting unclamped (wide-gamut) values is a separate
|
|
156
|
+
* decision. */
|
|
157
|
+
export function oklchToCssClamped(l: number, c: number, h: number): string {
|
|
158
|
+
const g = gamutClamp(l, c, h);
|
|
159
|
+
return oklchToCss(g.l, g.c, g.h);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const OKLCH_RE = /^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)(%?)\s+([\d.]+)(?:deg)?\s*\)$/i;
|
|
163
|
+
const HEX6_RE = /^#[0-9a-f]{6}$/i;
|
|
164
|
+
|
|
165
|
+
export function parseOklchCss(value: string): Oklch | null {
|
|
166
|
+
const m = OKLCH_RE.exec(value.trim());
|
|
167
|
+
if (!m) return null;
|
|
168
|
+
const l = Number.parseFloat(m[1]) / (m[2] === '%' ? 100 : 1);
|
|
169
|
+
// Percentage chroma is relative to 0.4 per the CSS Color 4 definition.
|
|
170
|
+
const c = Number.parseFloat(m[3]) * (m[4] === '%' ? 0.004 : 1);
|
|
171
|
+
return { l, c, h: Number.parseFloat(m[5]) };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Input boundary for color strings arriving from CSS: accepts the `oklch()`
|
|
175
|
+
* this system now emits and the 6-digit hex it emitted before the flip, so
|
|
176
|
+
* themes promoted either side of it still reconcile. */
|
|
177
|
+
export function cssColorToOklch(value: string): Oklch | null {
|
|
178
|
+
const raw = value.trim();
|
|
179
|
+
if (HEX6_RE.test(raw)) return hexToOklch(raw);
|
|
180
|
+
return parseOklchCss(raw);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function oklchToRgb255(l: number, c: number, h: number): { r: number; g: number; b: number } {
|
|
184
|
+
const clamped = gamutClamp(l, c, h);
|
|
185
|
+
const [L, a, bVal] = oklchToOklab(clamped.l, clamped.c, clamped.h);
|
|
186
|
+
const [r, g, b] = oklabToLinearRgb(L, a, bVal);
|
|
187
|
+
const to255 = (channel: number) => Math.round(Math.max(0, Math.min(1, linearToSrgb(channel))) * 255);
|
|
188
|
+
return { r: to255(r), g: to255(g), b: to255(b) };
|
|
189
|
+
}
|