@multiplatform.one/theme 6.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/CHANGELOG.md +18 -0
- package/LICENSE +201 -0
- package/package.json +85 -0
- package/src/audit/baselines/home-login.json +171 -0
- package/src/audit/baselines/home-login.png +0 -0
- package/src/audit/baselines/home-pokemon.json +355 -0
- package/src/audit/baselines/home-pokemon.png +0 -0
- package/src/audit/baselines/home.json +194 -0
- package/src/audit/baselines/home.png +0 -0
- package/src/audit/baselines/pokemon.json +251 -0
- package/src/audit/baselines/pokemon.png +0 -0
- package/src/audit/constraintAudit.ts +874 -0
- package/src/audit/diffReport.spec.ts +191 -0
- package/src/audit/diffReport.ts +344 -0
- package/src/audit/index.ts +10 -0
- package/src/font.ts +49 -0
- package/src/fonts/createFontLoader.ts +15 -0
- package/src/fonts/createFontLoader.web.ts +9 -0
- package/src/fonts/index.ts +2 -0
- package/src/fonts/types.ts +13 -0
- package/src/index.ts +3 -0
- package/src/theme/Intent.tsx +30 -0
- package/src/theme/Preset.tsx +93 -0
- package/src/theme/PresetContext.ts +27 -0
- package/src/theme/Tint.tsx +44 -0
- package/src/theme/__snapshots__/resolveKnobs.spec.ts.snap +69 -0
- package/src/theme/animations/css.ts +25 -0
- package/src/theme/animations/index.ts +1 -0
- package/src/theme/animations/index.web.ts +1 -0
- package/src/theme/animations/reactNative.ts +54 -0
- package/src/theme/colorRules.spec.ts +195 -0
- package/src/theme/colorRules.ts +166 -0
- package/src/theme/cookies.spec.ts +185 -0
- package/src/theme/cookies.ts +209 -0
- package/src/theme/createDefaultThemeConfig.ts +86 -0
- package/src/theme/createThemes.ts +286 -0
- package/src/theme/devtools/ColorLineVisualizer.tsx +261 -0
- package/src/theme/devtools/ThemeDevtoolsPanel.tsx +1657 -0
- package/src/theme/devtools/buildSnapshot.spec.ts +113 -0
- package/src/theme/devtools/buildSnapshot.ts +103 -0
- package/src/theme/devtools/knobIcons.tsx +217 -0
- package/src/theme/devtools/vizConstants.spec.ts +58 -0
- package/src/theme/devtools/vizConstants.ts +28 -0
- package/src/theme/index.ts +21 -0
- package/src/theme/intent.spec.tsx +276 -0
- package/src/theme/intents.ts +22 -0
- package/src/theme/knobs.ts +147 -0
- package/src/theme/pokemonScreen.spec.ts +109 -0
- package/src/theme/preset.spec.tsx +242 -0
- package/src/theme/preset.types.ts +22 -0
- package/src/theme/presets.spec.ts +252 -0
- package/src/theme/presets.ts +49 -0
- package/src/theme/recipes.ts +69 -0
- package/src/theme/resolveKnobs.spec.ts +571 -0
- package/src/theme/resolveKnobs.ts +226 -0
- package/src/theme/shared.spec.ts +84 -0
- package/src/theme/shared.tsx +131 -0
- package/src/theme/theme.native.tsx +65 -0
- package/src/theme/theme.tsx +190 -0
- package/src/theme/tint.spec.tsx +179 -0
- package/src/theme/useColorScale.native.ts +7 -0
- package/src/theme/useColorScale.ts +29 -0
- package/src/theme/useResolvedKnobs.ts +121 -0
- package/src/theme/useTheme.native.ts +49 -0
- package/src/theme/useTheme.ts +213 -0
- package/tsconfig.json +11 -0
- package/vitest.config.mjs +9 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import * as Colors from "@tamagui/colors";
|
|
2
|
+
import {
|
|
3
|
+
createThemes as tamaguiCreateThemes,
|
|
4
|
+
type CreateThemesProps,
|
|
5
|
+
} from "@tamagui/theme-builder";
|
|
6
|
+
import { themes as tamaguiThemes } from "@tamagui/themes";
|
|
7
|
+
|
|
8
|
+
const grayTheme = {
|
|
9
|
+
dark_gray: tamaguiThemes.dark,
|
|
10
|
+
dark_gray_active: tamaguiThemes.dark_active,
|
|
11
|
+
dark_gray_alt1: tamaguiThemes.dark_alt1,
|
|
12
|
+
dark_gray_alt2: tamaguiThemes.dark_alt2,
|
|
13
|
+
light_gray: tamaguiThemes.light,
|
|
14
|
+
light_gray_active: tamaguiThemes.light_active,
|
|
15
|
+
light_gray_alt1: tamaguiThemes.light_alt1,
|
|
16
|
+
light_gray_alt2: tamaguiThemes.light_alt2,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface Shadows {
|
|
20
|
+
shadow1: string;
|
|
21
|
+
shadow2: string;
|
|
22
|
+
shadow3: string;
|
|
23
|
+
shadow4: string;
|
|
24
|
+
shadow5: string;
|
|
25
|
+
shadow6: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ThemeBuilderDefinition<
|
|
29
|
+
DarkPalette extends string[] = string[],
|
|
30
|
+
LightPalette extends string[] = string[],
|
|
31
|
+
> {
|
|
32
|
+
darkPalette: DarkPalette;
|
|
33
|
+
lightPalette: LightPalette;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BaseThemeBuilderDefinition<
|
|
37
|
+
DarkPalette extends string[] = string[],
|
|
38
|
+
LightPalette extends string[] = string[],
|
|
39
|
+
DarkShadows extends Shadows = Shadows,
|
|
40
|
+
LightShadows extends Shadows = Shadows,
|
|
41
|
+
> extends ThemeBuilderDefinition<DarkPalette, LightPalette> {
|
|
42
|
+
darkShadows?: DarkShadows;
|
|
43
|
+
lightShadows?: LightShadows;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface GetThemeProps {
|
|
47
|
+
name: string;
|
|
48
|
+
theme: Record<string, string>;
|
|
49
|
+
scheme?: "light" | "dark";
|
|
50
|
+
parentName: string;
|
|
51
|
+
parentNames: string[];
|
|
52
|
+
level: number;
|
|
53
|
+
palette?: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CreateThemesBuilderOptions {
|
|
57
|
+
grandChildrenThemes?: CreateThemesProps["grandChildrenThemes"];
|
|
58
|
+
getTheme?: (props: GetThemeProps) => Record<string, string | number>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const defaultLightShadows: Shadows = {
|
|
62
|
+
shadow1: "rgba(0,0,0,0.12)",
|
|
63
|
+
shadow2: "rgba(0,0,0,0.16)",
|
|
64
|
+
shadow3: "rgba(0,0,0,0.22)",
|
|
65
|
+
shadow4: "rgba(0,0,0,0.28)",
|
|
66
|
+
shadow5: "rgba(0,0,0,0.34)",
|
|
67
|
+
shadow6: "rgba(0,0,0,0.4)",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const defaultDarkShadows: Shadows = {
|
|
71
|
+
shadow1: "rgba(0,0,0,0.2)",
|
|
72
|
+
shadow2: "rgba(0,0,0,0.3)",
|
|
73
|
+
shadow3: "rgba(0,0,0,0.4)",
|
|
74
|
+
shadow4: "rgba(0,0,0,0.5)",
|
|
75
|
+
shadow5: "rgba(0,0,0,0.6)",
|
|
76
|
+
shadow6: "rgba(0,0,0,0.7)",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export function createThemesBuilder(
|
|
80
|
+
baseTheme: BaseThemeBuilderDefinition,
|
|
81
|
+
accentTheme: ThemeBuilderDefinition,
|
|
82
|
+
options?: CreateThemesBuilderOptions,
|
|
83
|
+
) {
|
|
84
|
+
const base = {
|
|
85
|
+
palette: {
|
|
86
|
+
dark: baseTheme.darkPalette,
|
|
87
|
+
light: baseTheme.lightPalette,
|
|
88
|
+
},
|
|
89
|
+
extra: {
|
|
90
|
+
light: {
|
|
91
|
+
...Colors.amber,
|
|
92
|
+
...Colors.amberA,
|
|
93
|
+
...Colors.blue,
|
|
94
|
+
...Colors.blueA,
|
|
95
|
+
...Colors.bronze,
|
|
96
|
+
...Colors.bronzeA,
|
|
97
|
+
...Colors.brown,
|
|
98
|
+
...Colors.brownA,
|
|
99
|
+
...Colors.crimson,
|
|
100
|
+
...Colors.crimsonA,
|
|
101
|
+
...Colors.cyan,
|
|
102
|
+
...Colors.cyanA,
|
|
103
|
+
...Colors.gold,
|
|
104
|
+
...Colors.goldA,
|
|
105
|
+
...Colors.grass,
|
|
106
|
+
...Colors.grassA,
|
|
107
|
+
...Colors.gray,
|
|
108
|
+
...Colors.grayA,
|
|
109
|
+
...Colors.green,
|
|
110
|
+
...Colors.greenA,
|
|
111
|
+
...Colors.indigo,
|
|
112
|
+
...Colors.indigoA,
|
|
113
|
+
...Colors.lime,
|
|
114
|
+
...Colors.limeA,
|
|
115
|
+
...Colors.mauve,
|
|
116
|
+
...Colors.mauveA,
|
|
117
|
+
...Colors.mint,
|
|
118
|
+
...Colors.mintA,
|
|
119
|
+
...Colors.olive,
|
|
120
|
+
...Colors.oliveA,
|
|
121
|
+
...Colors.orange,
|
|
122
|
+
...Colors.orangeA,
|
|
123
|
+
...Colors.pink,
|
|
124
|
+
...Colors.pinkA,
|
|
125
|
+
...Colors.plum,
|
|
126
|
+
...Colors.plumA,
|
|
127
|
+
...Colors.purple,
|
|
128
|
+
...Colors.purpleA,
|
|
129
|
+
...Colors.red,
|
|
130
|
+
...Colors.redA,
|
|
131
|
+
...Colors.sage,
|
|
132
|
+
...Colors.sageA,
|
|
133
|
+
...Colors.sand,
|
|
134
|
+
...Colors.sandA,
|
|
135
|
+
...Colors.sky,
|
|
136
|
+
...Colors.skyA,
|
|
137
|
+
...Colors.slate,
|
|
138
|
+
...Colors.slateA,
|
|
139
|
+
...Colors.teal,
|
|
140
|
+
...Colors.tealA,
|
|
141
|
+
...Colors.tomato,
|
|
142
|
+
...Colors.tomatoA,
|
|
143
|
+
...Colors.violet,
|
|
144
|
+
...Colors.violetA,
|
|
145
|
+
...Colors.yellow,
|
|
146
|
+
...Colors.yellowA,
|
|
147
|
+
...baseTheme.lightShadows,
|
|
148
|
+
shadowColor: baseTheme.lightShadows?.shadow1 || defaultLightShadows.shadow1,
|
|
149
|
+
},
|
|
150
|
+
dark: {
|
|
151
|
+
...Colors.amberDark,
|
|
152
|
+
...Colors.amberDarkA,
|
|
153
|
+
...Colors.blueDark,
|
|
154
|
+
...Colors.blueDarkA,
|
|
155
|
+
...Colors.bronzeDark,
|
|
156
|
+
...Colors.bronzeDarkA,
|
|
157
|
+
...Colors.brownDark,
|
|
158
|
+
...Colors.brownDarkA,
|
|
159
|
+
...Colors.crimsonDark,
|
|
160
|
+
...Colors.crimsonDarkA,
|
|
161
|
+
...Colors.cyanDark,
|
|
162
|
+
...Colors.cyanDarkA,
|
|
163
|
+
...Colors.goldDark,
|
|
164
|
+
...Colors.goldDarkA,
|
|
165
|
+
...Colors.grassDark,
|
|
166
|
+
...Colors.grassDarkA,
|
|
167
|
+
...Colors.grayDark,
|
|
168
|
+
...Colors.grayDarkA,
|
|
169
|
+
...Colors.greenDark,
|
|
170
|
+
...Colors.greenDarkA,
|
|
171
|
+
...Colors.indigoDark,
|
|
172
|
+
...Colors.indigoDarkA,
|
|
173
|
+
...Colors.limeDark,
|
|
174
|
+
...Colors.limeDarkA,
|
|
175
|
+
...Colors.mauveDark,
|
|
176
|
+
...Colors.mauveDarkA,
|
|
177
|
+
...Colors.mintDark,
|
|
178
|
+
...Colors.mintDarkA,
|
|
179
|
+
...Colors.oliveDark,
|
|
180
|
+
...Colors.oliveDarkA,
|
|
181
|
+
...Colors.orangeDark,
|
|
182
|
+
...Colors.orangeDarkA,
|
|
183
|
+
...Colors.pinkDark,
|
|
184
|
+
...Colors.pinkDarkA,
|
|
185
|
+
...Colors.plumDark,
|
|
186
|
+
...Colors.plumDarkA,
|
|
187
|
+
...Colors.purpleDark,
|
|
188
|
+
...Colors.purpleDarkA,
|
|
189
|
+
...Colors.redDark,
|
|
190
|
+
...Colors.redDarkA,
|
|
191
|
+
...Colors.sageDark,
|
|
192
|
+
...Colors.sageDarkA,
|
|
193
|
+
...Colors.sandDark,
|
|
194
|
+
...Colors.sandDarkA,
|
|
195
|
+
...Colors.skyDark,
|
|
196
|
+
...Colors.skyDarkA,
|
|
197
|
+
...Colors.slateDark,
|
|
198
|
+
...Colors.slateDarkA,
|
|
199
|
+
...Colors.tealDark,
|
|
200
|
+
...Colors.tealDarkA,
|
|
201
|
+
...Colors.tomatoDark,
|
|
202
|
+
...Colors.tomatoDarkA,
|
|
203
|
+
...Colors.violetDark,
|
|
204
|
+
...Colors.violetDarkA,
|
|
205
|
+
...Colors.yellowDark,
|
|
206
|
+
...Colors.yellowDarkA,
|
|
207
|
+
...baseTheme.darkShadows,
|
|
208
|
+
shadowColor: baseTheme.darkShadows?.shadow1 || defaultDarkShadows.shadow1,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
const childrenThemes = {
|
|
213
|
+
warning: {
|
|
214
|
+
palette: {
|
|
215
|
+
dark: Object.values(Colors.yellowDark),
|
|
216
|
+
light: Object.values(Colors.yellow),
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
error: {
|
|
220
|
+
palette: {
|
|
221
|
+
dark: Object.values(Colors.redDark),
|
|
222
|
+
light: Object.values(Colors.red),
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
success: {
|
|
226
|
+
palette: {
|
|
227
|
+
dark: Object.values(Colors.greenDark),
|
|
228
|
+
light: Object.values(Colors.green),
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
const createThemesProps: Record<string, unknown> = {
|
|
233
|
+
base,
|
|
234
|
+
childrenThemes,
|
|
235
|
+
accent: {
|
|
236
|
+
palette: {
|
|
237
|
+
dark: accentTheme.darkPalette,
|
|
238
|
+
light: accentTheme.lightPalette,
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
if (options?.grandChildrenThemes) {
|
|
243
|
+
createThemesProps.grandChildrenThemes = options.grandChildrenThemes;
|
|
244
|
+
}
|
|
245
|
+
if (options?.getTheme) {
|
|
246
|
+
createThemesProps.getTheme = options.getTheme;
|
|
247
|
+
}
|
|
248
|
+
const initialTheme = tamaguiCreateThemes(
|
|
249
|
+
createThemesProps as Parameters<typeof tamaguiCreateThemes>[0],
|
|
250
|
+
);
|
|
251
|
+
const themes = {
|
|
252
|
+
...grayTheme,
|
|
253
|
+
...tamaguiThemes,
|
|
254
|
+
...initialTheme,
|
|
255
|
+
};
|
|
256
|
+
return {
|
|
257
|
+
themes() {
|
|
258
|
+
return themes;
|
|
259
|
+
},
|
|
260
|
+
addTheme<K extends string>(name: K, theme: ThemeBuilderDefinition) {
|
|
261
|
+
const addThemeProps: Record<string, unknown> = {
|
|
262
|
+
base,
|
|
263
|
+
childrenThemes,
|
|
264
|
+
accent: {
|
|
265
|
+
palette: {
|
|
266
|
+
dark: theme.darkPalette,
|
|
267
|
+
light: theme.lightPalette,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
if (options?.grandChildrenThemes) {
|
|
272
|
+
addThemeProps.grandChildrenThemes = options.grandChildrenThemes;
|
|
273
|
+
}
|
|
274
|
+
if (options?.getTheme) {
|
|
275
|
+
addThemeProps.getTheme = options.getTheme;
|
|
276
|
+
}
|
|
277
|
+
const createdTheme = tamaguiCreateThemes(
|
|
278
|
+
addThemeProps as Parameters<typeof tamaguiCreateThemes>[0],
|
|
279
|
+
);
|
|
280
|
+
return {
|
|
281
|
+
[`dark_${name}`]: createdTheme.dark_accent,
|
|
282
|
+
[`light_${name}`]: createdTheme.light_accent,
|
|
283
|
+
} as Record<`dark_${K}` | `light_${K}`, typeof createdTheme.dark_accent>;
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color palette visualizer for devtools.
|
|
3
|
+
*
|
|
4
|
+
* Shows the 12-step color scale with semantic group brackets (Background,
|
|
5
|
+
* UI Background, Border, Primary, Text) all on a single row above the bar.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useEffect, useMemo, useState, type CSSProperties } from "react";
|
|
9
|
+
import { semanticGroups } from "../colorRules";
|
|
10
|
+
import type { Knobs } from "../knobs";
|
|
11
|
+
import { contrastTextFor, cellSize } from "./vizConstants";
|
|
12
|
+
|
|
13
|
+
// ── Helpers ──────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
function readColorsFromElement(el: Element): string[] | null {
|
|
16
|
+
const style = getComputedStyle(el);
|
|
17
|
+
const result: string[] = [];
|
|
18
|
+
let found = false;
|
|
19
|
+
for (let i = 1; i <= 12; i++) {
|
|
20
|
+
let color = style.getPropertyValue(`--color${i}`).trim();
|
|
21
|
+
if (!color) color = style.getPropertyValue(`--color-${i}`).trim();
|
|
22
|
+
if (color) {
|
|
23
|
+
found = true;
|
|
24
|
+
result.push(color);
|
|
25
|
+
} else {
|
|
26
|
+
result.push(fallbackGradient()[i - 1]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return found ? result : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function useDomThemeColors(): string[] {
|
|
33
|
+
const [colors, setColors] = useState<string[]>(() => fallbackGradient());
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const readColors = () => {
|
|
37
|
+
let result = readColorsFromElement(document.documentElement);
|
|
38
|
+
if (!result && document.body) {
|
|
39
|
+
result = readColorsFromElement(document.body);
|
|
40
|
+
}
|
|
41
|
+
if (!result) {
|
|
42
|
+
const themed = document.querySelector('[class*="t_"]');
|
|
43
|
+
if (themed) result = readColorsFromElement(themed);
|
|
44
|
+
}
|
|
45
|
+
if (!result) {
|
|
46
|
+
const root = document.getElementById("root") || document.getElementById("__next");
|
|
47
|
+
if (root) result = readColorsFromElement(root);
|
|
48
|
+
}
|
|
49
|
+
if (result) setColors(result);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
readColors();
|
|
53
|
+
const observer = new MutationObserver(readColors);
|
|
54
|
+
observer.observe(document.documentElement, {
|
|
55
|
+
attributes: true,
|
|
56
|
+
attributeFilter: ["class", "style"],
|
|
57
|
+
});
|
|
58
|
+
if (document.body) {
|
|
59
|
+
observer.observe(document.body, {
|
|
60
|
+
attributes: true,
|
|
61
|
+
attributeFilter: ["class", "style"],
|
|
62
|
+
subtree: true,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return () => observer.disconnect();
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
return colors;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function fallbackGradient(): string[] {
|
|
72
|
+
return Array.from({ length: 12 }, (_, i) => {
|
|
73
|
+
const t = i / 11;
|
|
74
|
+
const v = Math.round(240 - t * 200);
|
|
75
|
+
return `rgb(${v},${v},${v})`;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Sub-components ──────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
function NumberLineRow({
|
|
82
|
+
colorFor,
|
|
83
|
+
compact,
|
|
84
|
+
}: {
|
|
85
|
+
colorFor: (step: number) => string;
|
|
86
|
+
compact?: boolean;
|
|
87
|
+
}) {
|
|
88
|
+
const h = compact ? Math.round(cellSize / 2) : cellSize;
|
|
89
|
+
return (
|
|
90
|
+
<div style={{ display: "flex", gap: 0 }}>
|
|
91
|
+
{Array.from({ length: 12 }, (_, i) => {
|
|
92
|
+
const step = i + 1;
|
|
93
|
+
const background = colorFor(step);
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
key={step}
|
|
97
|
+
style={{
|
|
98
|
+
width: cellSize,
|
|
99
|
+
height: h,
|
|
100
|
+
flexShrink: 0,
|
|
101
|
+
background: background,
|
|
102
|
+
color: contrastTextFor(background),
|
|
103
|
+
display: "flex",
|
|
104
|
+
alignItems: "center",
|
|
105
|
+
justifyContent: "center",
|
|
106
|
+
fontSize: compact ? 9 : 11,
|
|
107
|
+
fontWeight: 700,
|
|
108
|
+
}}
|
|
109
|
+
title={`$color${step}`}
|
|
110
|
+
>
|
|
111
|
+
{step}
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
})}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** All semantic group brackets rendered on a single row above the color bar. */
|
|
120
|
+
function SemanticBrackets() {
|
|
121
|
+
const totalWidth = 12 * cellSize;
|
|
122
|
+
return (
|
|
123
|
+
<div style={{ position: "relative", height: 24, width: totalWidth, marginBottom: 2 }}>
|
|
124
|
+
{semanticGroups.map((g) => {
|
|
125
|
+
const leftPx = (g.from - 1) * cellSize;
|
|
126
|
+
const widthPx = (g.to - g.from + 1) * cellSize;
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
key={g.key}
|
|
130
|
+
style={{
|
|
131
|
+
position: "absolute",
|
|
132
|
+
left: leftPx,
|
|
133
|
+
width: widthPx,
|
|
134
|
+
bottom: 0,
|
|
135
|
+
display: "flex",
|
|
136
|
+
flexDirection: "column",
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
<div
|
|
140
|
+
style={{
|
|
141
|
+
textAlign: "center",
|
|
142
|
+
fontSize: 9,
|
|
143
|
+
fontWeight: 600,
|
|
144
|
+
color: g.color,
|
|
145
|
+
lineHeight: 1.2,
|
|
146
|
+
whiteSpace: "nowrap",
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{g.label}
|
|
150
|
+
</div>
|
|
151
|
+
<div
|
|
152
|
+
style={{
|
|
153
|
+
height: 6,
|
|
154
|
+
borderLeft: `2px solid ${g.color}`,
|
|
155
|
+
borderRight: `2px solid ${g.color}`,
|
|
156
|
+
borderTop: `2px solid ${g.color}`,
|
|
157
|
+
borderRadius: "3px 3px 0 0",
|
|
158
|
+
}}
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
})}
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── Styles ──────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
function createVizStyles(isDark: boolean) {
|
|
170
|
+
const border = isDark ? "#333" : "#ddd";
|
|
171
|
+
const mutedText = isDark ? "#888" : "#999";
|
|
172
|
+
const foreground = isDark ? "#e8e8e8" : "#1a1a1a";
|
|
173
|
+
const cardBackground = isDark ? "#1e1e1e" : "#fafafa";
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
container: {
|
|
177
|
+
display: "flex" as const,
|
|
178
|
+
flexDirection: "column" as const,
|
|
179
|
+
gap: 6,
|
|
180
|
+
padding: "8px 10px",
|
|
181
|
+
borderRadius: 8,
|
|
182
|
+
border: `1px solid ${border}`,
|
|
183
|
+
background: cardBackground,
|
|
184
|
+
width: "fit-content" as const,
|
|
185
|
+
fontSize: 11,
|
|
186
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
187
|
+
color: foreground,
|
|
188
|
+
},
|
|
189
|
+
title: {
|
|
190
|
+
fontSize: 13,
|
|
191
|
+
fontWeight: 600,
|
|
192
|
+
textTransform: "uppercase" as const,
|
|
193
|
+
letterSpacing: "0.06em",
|
|
194
|
+
color: mutedText,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ── Main component ──────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
export interface ColorLineVisualizerProps {
|
|
202
|
+
knobs: Knobs;
|
|
203
|
+
isDark: boolean;
|
|
204
|
+
themeColors?: string[];
|
|
205
|
+
oppositeThemeColors?: string[];
|
|
206
|
+
accentColors?: string[];
|
|
207
|
+
oppositeAccentColors?: string[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function ColorLineVisualizer({
|
|
211
|
+
knobs: _knobs,
|
|
212
|
+
isDark,
|
|
213
|
+
themeColors: propColors,
|
|
214
|
+
oppositeThemeColors: propOppositeColors,
|
|
215
|
+
accentColors: propAccentColors,
|
|
216
|
+
oppositeAccentColors: propOppositeAccentColors,
|
|
217
|
+
}: ColorLineVisualizerProps) {
|
|
218
|
+
const domColors = useDomThemeColors();
|
|
219
|
+
const themeColors = propColors && propColors.length === 12 ? propColors : domColors;
|
|
220
|
+
const styles = useMemo(() => createVizStyles(isDark), [isDark]);
|
|
221
|
+
|
|
222
|
+
const toColorFn =
|
|
223
|
+
(arr: string[]) =>
|
|
224
|
+
(step: number): string =>
|
|
225
|
+
arr[Math.min(11, Math.max(0, step - 1))];
|
|
226
|
+
|
|
227
|
+
const colorFor = toColorFn(themeColors);
|
|
228
|
+
const oppositeColors =
|
|
229
|
+
propOppositeColors && propOppositeColors.length === 12 ? propOppositeColors : null;
|
|
230
|
+
const accentColors = propAccentColors && propAccentColors.length === 12 ? propAccentColors : null;
|
|
231
|
+
const oppositeAccentColors =
|
|
232
|
+
propOppositeAccentColors && propOppositeAccentColors.length === 12
|
|
233
|
+
? propOppositeAccentColors
|
|
234
|
+
: null;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div style={styles.container}>
|
|
238
|
+
<div style={styles.title as CSSProperties}>Theme</div>
|
|
239
|
+
<SemanticBrackets />
|
|
240
|
+
<NumberLineRow colorFor={colorFor} />
|
|
241
|
+
{oppositeColors && <NumberLineRow colorFor={toColorFn(oppositeColors)} compact />}
|
|
242
|
+
{accentColors && (
|
|
243
|
+
<>
|
|
244
|
+
<div
|
|
245
|
+
style={{
|
|
246
|
+
...(styles.title as CSSProperties),
|
|
247
|
+
fontSize: 11,
|
|
248
|
+
marginTop: 6,
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
accent
|
|
252
|
+
</div>
|
|
253
|
+
<NumberLineRow colorFor={toColorFn(accentColors)} />
|
|
254
|
+
{oppositeAccentColors && (
|
|
255
|
+
<NumberLineRow colorFor={toColorFn(oppositeAccentColors)} compact />
|
|
256
|
+
)}
|
|
257
|
+
</>
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
}
|