react-mcu 1.3.0 → 1.3.2
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/dist/cli.js +729 -0
- package/dist/index.d.ts +15 -24
- package/dist/index.js +173 -186
- package/package.json +3 -4
- package/src/cli.ts +0 -116
package/dist/cli.js
ADDED
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { Command, Option } from "commander";
|
|
7
|
+
|
|
8
|
+
// src/lib/builder.ts
|
|
9
|
+
import {
|
|
10
|
+
argbFromHex,
|
|
11
|
+
Blend,
|
|
12
|
+
blueFromArgb,
|
|
13
|
+
DynamicColor,
|
|
14
|
+
DynamicScheme,
|
|
15
|
+
greenFromArgb,
|
|
16
|
+
Hct,
|
|
17
|
+
hexFromArgb,
|
|
18
|
+
MaterialDynamicColors,
|
|
19
|
+
redFromArgb,
|
|
20
|
+
SchemeContent,
|
|
21
|
+
SchemeExpressive,
|
|
22
|
+
SchemeFidelity,
|
|
23
|
+
SchemeMonochrome,
|
|
24
|
+
SchemeNeutral,
|
|
25
|
+
SchemeTonalSpot,
|
|
26
|
+
SchemeVibrant,
|
|
27
|
+
TonalPalette
|
|
28
|
+
} from "@material/material-color-utilities";
|
|
29
|
+
import { kebabCase, startCase, upperFirst } from "lodash-es";
|
|
30
|
+
function adjustToneForContrast(baseTone, contrastLevel, adjustmentFactor = DEFAULT_CONTRAST_ADJUSTMENT_FACTOR) {
|
|
31
|
+
if (contrastLevel === 0) return baseTone;
|
|
32
|
+
const distanceToCenter = baseTone - 50;
|
|
33
|
+
const delta = distanceToCenter * contrastLevel * adjustmentFactor;
|
|
34
|
+
const adjustedTone = baseTone + delta;
|
|
35
|
+
return Math.max(0, Math.min(100, adjustedTone));
|
|
36
|
+
}
|
|
37
|
+
var schemeNames = [
|
|
38
|
+
"tonalSpot",
|
|
39
|
+
"monochrome",
|
|
40
|
+
"neutral",
|
|
41
|
+
"vibrant",
|
|
42
|
+
"expressive",
|
|
43
|
+
"fidelity",
|
|
44
|
+
"content"
|
|
45
|
+
];
|
|
46
|
+
var schemesMap = {
|
|
47
|
+
tonalSpot: SchemeTonalSpot,
|
|
48
|
+
monochrome: SchemeMonochrome,
|
|
49
|
+
neutral: SchemeNeutral,
|
|
50
|
+
vibrant: SchemeVibrant,
|
|
51
|
+
expressive: SchemeExpressive,
|
|
52
|
+
fidelity: SchemeFidelity,
|
|
53
|
+
content: SchemeContent
|
|
54
|
+
};
|
|
55
|
+
var DEFAULT_SCHEME = "tonalSpot";
|
|
56
|
+
var DEFAULT_CONTRAST = 0;
|
|
57
|
+
var DEFAULT_CUSTOM_COLORS = [];
|
|
58
|
+
var DEFAULT_CONTRAST_ALL_COLORS = false;
|
|
59
|
+
var DEFAULT_ADAPTIVE_SHADES = false;
|
|
60
|
+
var DEFAULT_BLEND = true;
|
|
61
|
+
var DEFAULT_CONTRAST_ADJUSTMENT_FACTOR = 0.2;
|
|
62
|
+
var STANDARD_TONES = [
|
|
63
|
+
0,
|
|
64
|
+
5,
|
|
65
|
+
10,
|
|
66
|
+
15,
|
|
67
|
+
20,
|
|
68
|
+
25,
|
|
69
|
+
30,
|
|
70
|
+
35,
|
|
71
|
+
40,
|
|
72
|
+
50,
|
|
73
|
+
60,
|
|
74
|
+
70,
|
|
75
|
+
80,
|
|
76
|
+
90,
|
|
77
|
+
95,
|
|
78
|
+
98,
|
|
79
|
+
99,
|
|
80
|
+
100
|
|
81
|
+
];
|
|
82
|
+
var Variant = {
|
|
83
|
+
MONOCHROME: 0,
|
|
84
|
+
NEUTRAL: 1,
|
|
85
|
+
TONAL_SPOT: 2,
|
|
86
|
+
VIBRANT: 3,
|
|
87
|
+
EXPRESSIVE: 4,
|
|
88
|
+
FIDELITY: 5,
|
|
89
|
+
CONTENT: 6,
|
|
90
|
+
RAINBOW: 7,
|
|
91
|
+
FRUIT_SALAD: 8
|
|
92
|
+
};
|
|
93
|
+
var schemeToVariant = {
|
|
94
|
+
monochrome: Variant.MONOCHROME,
|
|
95
|
+
neutral: Variant.NEUTRAL,
|
|
96
|
+
tonalSpot: Variant.TONAL_SPOT,
|
|
97
|
+
vibrant: Variant.VIBRANT,
|
|
98
|
+
expressive: Variant.EXPRESSIVE,
|
|
99
|
+
fidelity: Variant.FIDELITY,
|
|
100
|
+
content: Variant.CONTENT
|
|
101
|
+
};
|
|
102
|
+
var tokenNames = [
|
|
103
|
+
"background",
|
|
104
|
+
"error",
|
|
105
|
+
"errorContainer",
|
|
106
|
+
"inverseOnSurface",
|
|
107
|
+
"inversePrimary",
|
|
108
|
+
"inverseSurface",
|
|
109
|
+
"onBackground",
|
|
110
|
+
"onError",
|
|
111
|
+
"onErrorContainer",
|
|
112
|
+
"onPrimary",
|
|
113
|
+
"onPrimaryContainer",
|
|
114
|
+
"onPrimaryFixed",
|
|
115
|
+
"onPrimaryFixedVariant",
|
|
116
|
+
"onSecondary",
|
|
117
|
+
"onSecondaryContainer",
|
|
118
|
+
"onSecondaryFixed",
|
|
119
|
+
"onSecondaryFixedVariant",
|
|
120
|
+
"onSurface",
|
|
121
|
+
"onSurfaceVariant",
|
|
122
|
+
"onTertiary",
|
|
123
|
+
"onTertiaryContainer",
|
|
124
|
+
"onTertiaryFixed",
|
|
125
|
+
"onTertiaryFixedVariant",
|
|
126
|
+
"outline",
|
|
127
|
+
"outlineVariant",
|
|
128
|
+
"primary",
|
|
129
|
+
"primaryContainer",
|
|
130
|
+
"primaryFixed",
|
|
131
|
+
"primaryFixedDim",
|
|
132
|
+
"scrim",
|
|
133
|
+
"secondary",
|
|
134
|
+
"secondaryContainer",
|
|
135
|
+
"secondaryFixed",
|
|
136
|
+
"secondaryFixedDim",
|
|
137
|
+
"shadow",
|
|
138
|
+
"surface",
|
|
139
|
+
"surfaceBright",
|
|
140
|
+
"surfaceContainer",
|
|
141
|
+
"surfaceContainerHigh",
|
|
142
|
+
"surfaceContainerHighest",
|
|
143
|
+
"surfaceContainerLow",
|
|
144
|
+
"surfaceContainerLowest",
|
|
145
|
+
"surfaceDim",
|
|
146
|
+
"surfaceTint",
|
|
147
|
+
"surfaceVariant",
|
|
148
|
+
"tertiary",
|
|
149
|
+
"tertiaryContainer",
|
|
150
|
+
"tertiaryFixed",
|
|
151
|
+
"tertiaryFixedDim"
|
|
152
|
+
];
|
|
153
|
+
function toRecord(arr, getEntry) {
|
|
154
|
+
return Object.fromEntries(arr.map(getEntry));
|
|
155
|
+
}
|
|
156
|
+
function getPalette(palettes, colorName) {
|
|
157
|
+
const palette = palettes[colorName];
|
|
158
|
+
if (!palette) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Custom color palette not found for '${colorName}'. This is likely a bug in the implementation.`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return palette;
|
|
164
|
+
}
|
|
165
|
+
function mergeBaseAndCustomColors(scheme, customColors, colorPalettes, contrastAllColors) {
|
|
166
|
+
const baseVars = toRecord(tokenNames, (tokenName) => {
|
|
167
|
+
const dynamicColor = MaterialDynamicColors[tokenName];
|
|
168
|
+
const argb = dynamicColor.getArgb(scheme);
|
|
169
|
+
return [tokenName, argb];
|
|
170
|
+
});
|
|
171
|
+
const customVars = {};
|
|
172
|
+
customColors.forEach((color) => {
|
|
173
|
+
const colorname = color.name;
|
|
174
|
+
const getPaletteForColor = (s) => getPalette(colorPalettes, colorname);
|
|
175
|
+
const getTone = (baseTone) => (s) => {
|
|
176
|
+
if (!contrastAllColors) return baseTone;
|
|
177
|
+
return adjustToneForContrast(baseTone, s.contrastLevel);
|
|
178
|
+
};
|
|
179
|
+
const colorDynamicColor = new DynamicColor(
|
|
180
|
+
colorname,
|
|
181
|
+
getPaletteForColor,
|
|
182
|
+
(s) => getTone(s.isDark ? 80 : 40)(s),
|
|
183
|
+
// Main color: lighter in dark mode, darker in light mode
|
|
184
|
+
true
|
|
185
|
+
// background
|
|
186
|
+
);
|
|
187
|
+
const onColorDynamicColor = new DynamicColor(
|
|
188
|
+
`on${upperFirst(colorname)}`,
|
|
189
|
+
getPaletteForColor,
|
|
190
|
+
(s) => getTone(s.isDark ? 20 : 100)(s),
|
|
191
|
+
// Text on main color: high contrast (dark on light, light on dark)
|
|
192
|
+
false
|
|
193
|
+
);
|
|
194
|
+
const containerDynamicColor = new DynamicColor(
|
|
195
|
+
`${colorname}Container`,
|
|
196
|
+
getPaletteForColor,
|
|
197
|
+
(s) => getTone(s.isDark ? 30 : 90)(s),
|
|
198
|
+
// Container: subtle variant (darker in dark mode, lighter in light mode)
|
|
199
|
+
true
|
|
200
|
+
// background
|
|
201
|
+
);
|
|
202
|
+
const onContainerDynamicColor = new DynamicColor(
|
|
203
|
+
`on${upperFirst(colorname)}Container`,
|
|
204
|
+
getPaletteForColor,
|
|
205
|
+
(s) => getTone(s.isDark ? 90 : 30)(s),
|
|
206
|
+
// Text on container: high contrast against container background
|
|
207
|
+
false
|
|
208
|
+
);
|
|
209
|
+
customVars[colorname] = colorDynamicColor.getArgb(scheme);
|
|
210
|
+
customVars[`on${upperFirst(colorname)}`] = onColorDynamicColor.getArgb(scheme);
|
|
211
|
+
customVars[`${colorname}Container`] = containerDynamicColor.getArgb(scheme);
|
|
212
|
+
customVars[`on${upperFirst(colorname)}Container`] = onContainerDynamicColor.getArgb(scheme);
|
|
213
|
+
});
|
|
214
|
+
return { ...baseVars, ...customVars };
|
|
215
|
+
}
|
|
216
|
+
function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonization) {
|
|
217
|
+
const colorArgb = argbFromHex(colorDef.hex);
|
|
218
|
+
const harmonizedArgb = colorDef.blend ? Blend.harmonize(colorArgb, effectiveSourceForHarmonization) : colorArgb;
|
|
219
|
+
const hct = Hct.fromInt(harmonizedArgb);
|
|
220
|
+
let targetChroma;
|
|
221
|
+
if (colorDef.core && colorDef.chromaSource) {
|
|
222
|
+
if (colorDef.chromaSource === "neutral") {
|
|
223
|
+
targetChroma = baseScheme.neutralPalette.chroma;
|
|
224
|
+
} else if (colorDef.chromaSource === "neutralVariant") {
|
|
225
|
+
targetChroma = baseScheme.neutralVariantPalette.chroma;
|
|
226
|
+
} else {
|
|
227
|
+
targetChroma = baseScheme.primaryPalette.chroma;
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
targetChroma = baseScheme.primaryPalette.chroma;
|
|
231
|
+
}
|
|
232
|
+
return TonalPalette.fromHueAndChroma(hct.hue, targetChroma);
|
|
233
|
+
}
|
|
234
|
+
function builder(hexSource, {
|
|
235
|
+
scheme = DEFAULT_SCHEME,
|
|
236
|
+
contrast = DEFAULT_CONTRAST,
|
|
237
|
+
primary,
|
|
238
|
+
secondary,
|
|
239
|
+
tertiary,
|
|
240
|
+
neutral,
|
|
241
|
+
neutralVariant,
|
|
242
|
+
error,
|
|
243
|
+
customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS,
|
|
244
|
+
contrastAllColors = DEFAULT_CONTRAST_ALL_COLORS,
|
|
245
|
+
adaptiveShades = DEFAULT_ADAPTIVE_SHADES
|
|
246
|
+
} = {}) {
|
|
247
|
+
const sourceArgb = argbFromHex(hexSource);
|
|
248
|
+
const sourceHct = Hct.fromInt(sourceArgb);
|
|
249
|
+
const effectiveSource = primary || hexSource;
|
|
250
|
+
const effectiveSourceArgb = argbFromHex(effectiveSource);
|
|
251
|
+
const effectiveSourceForHarmonization = primary ? argbFromHex(primary) : sourceArgb;
|
|
252
|
+
const SchemeClass = schemesMap[scheme];
|
|
253
|
+
const primaryHct = Hct.fromInt(effectiveSourceArgb);
|
|
254
|
+
const baseScheme = new SchemeClass(primaryHct, false, contrast);
|
|
255
|
+
const allColors = [
|
|
256
|
+
// Core colors (hex may be undefined)
|
|
257
|
+
{
|
|
258
|
+
name: "primary",
|
|
259
|
+
hex: primary,
|
|
260
|
+
core: true,
|
|
261
|
+
chromaSource: "primary"
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: "secondary",
|
|
265
|
+
hex: secondary,
|
|
266
|
+
core: true,
|
|
267
|
+
chromaSource: "primary"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "tertiary",
|
|
271
|
+
hex: tertiary,
|
|
272
|
+
core: true,
|
|
273
|
+
chromaSource: "primary"
|
|
274
|
+
},
|
|
275
|
+
{ name: "error", hex: error, core: true, chromaSource: "primary" },
|
|
276
|
+
{
|
|
277
|
+
name: "neutral",
|
|
278
|
+
hex: neutral,
|
|
279
|
+
core: true,
|
|
280
|
+
chromaSource: "neutral"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "neutralVariant",
|
|
284
|
+
hex: neutralVariant,
|
|
285
|
+
core: true,
|
|
286
|
+
chromaSource: "neutralVariant"
|
|
287
|
+
},
|
|
288
|
+
//
|
|
289
|
+
// Custom colors
|
|
290
|
+
//
|
|
291
|
+
...hexCustomColors.map((c) => ({
|
|
292
|
+
name: c.name,
|
|
293
|
+
hex: c.hex,
|
|
294
|
+
blend: c.blend,
|
|
295
|
+
core: false
|
|
296
|
+
}))
|
|
297
|
+
];
|
|
298
|
+
const definedColors = allColors.filter(
|
|
299
|
+
(c) => c.hex !== void 0
|
|
300
|
+
);
|
|
301
|
+
const colorPalettes = Object.fromEntries(
|
|
302
|
+
definedColors.map((colorDef) => [
|
|
303
|
+
colorDef.name,
|
|
304
|
+
createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonization)
|
|
305
|
+
])
|
|
306
|
+
);
|
|
307
|
+
const variant = schemeToVariant[scheme];
|
|
308
|
+
const schemeConfig = {
|
|
309
|
+
sourceColorArgb: effectiveSourceArgb,
|
|
310
|
+
variant,
|
|
311
|
+
contrastLevel: contrast,
|
|
312
|
+
primaryPalette: colorPalettes["primary"] || baseScheme.primaryPalette,
|
|
313
|
+
secondaryPalette: colorPalettes["secondary"] || baseScheme.secondaryPalette,
|
|
314
|
+
tertiaryPalette: colorPalettes["tertiary"] || baseScheme.tertiaryPalette,
|
|
315
|
+
neutralPalette: colorPalettes["neutral"] || baseScheme.neutralPalette,
|
|
316
|
+
neutralVariantPalette: colorPalettes["neutralVariant"] || baseScheme.neutralVariantPalette
|
|
317
|
+
};
|
|
318
|
+
const lightScheme = new DynamicScheme({ ...schemeConfig, isDark: false });
|
|
319
|
+
const darkScheme = new DynamicScheme({ ...schemeConfig, isDark: true });
|
|
320
|
+
const errorPalette = colorPalettes["error"];
|
|
321
|
+
if (errorPalette) {
|
|
322
|
+
lightScheme.errorPalette = errorPalette;
|
|
323
|
+
darkScheme.errorPalette = errorPalette;
|
|
324
|
+
}
|
|
325
|
+
const allPalettes = {
|
|
326
|
+
primary: lightScheme.primaryPalette,
|
|
327
|
+
secondary: lightScheme.secondaryPalette,
|
|
328
|
+
tertiary: lightScheme.tertiaryPalette,
|
|
329
|
+
error: lightScheme.errorPalette,
|
|
330
|
+
neutral: lightScheme.neutralPalette,
|
|
331
|
+
"neutral-variant": lightScheme.neutralVariantPalette,
|
|
332
|
+
// Add custom color palettes
|
|
333
|
+
...Object.fromEntries(
|
|
334
|
+
definedColors.filter((c) => !c.core).map((colorDef) => [colorDef.name, colorPalettes[colorDef.name]])
|
|
335
|
+
)
|
|
336
|
+
};
|
|
337
|
+
const customColors = definedColors.filter((c) => !c.core).map((c) => ({
|
|
338
|
+
name: c.name,
|
|
339
|
+
blend: c.blend ?? DEFAULT_BLEND,
|
|
340
|
+
value: argbFromHex(c.hex)
|
|
341
|
+
}));
|
|
342
|
+
const mergedColorsLight = mergeBaseAndCustomColors(
|
|
343
|
+
lightScheme,
|
|
344
|
+
customColors,
|
|
345
|
+
colorPalettes,
|
|
346
|
+
contrastAllColors
|
|
347
|
+
);
|
|
348
|
+
const mergedColorsDark = mergeBaseAndCustomColors(
|
|
349
|
+
darkScheme,
|
|
350
|
+
customColors,
|
|
351
|
+
colorPalettes,
|
|
352
|
+
contrastAllColors
|
|
353
|
+
);
|
|
354
|
+
return {
|
|
355
|
+
//
|
|
356
|
+
// ██████ ███████ ███████
|
|
357
|
+
// ██ ██ ██
|
|
358
|
+
// ██ ███████ ███████
|
|
359
|
+
// ██ ██ ██
|
|
360
|
+
// ██████ ███████ ███████
|
|
361
|
+
//
|
|
362
|
+
toCss() {
|
|
363
|
+
function cssVar(colorName, colorValue) {
|
|
364
|
+
const name = `--mcu-${kebabCase(colorName)}`;
|
|
365
|
+
const value = hexFromArgb(colorValue);
|
|
366
|
+
return `${name}:${value};`;
|
|
367
|
+
}
|
|
368
|
+
function toCssVars(mergedColors) {
|
|
369
|
+
return Object.entries(mergedColors).map(([name, value]) => cssVar(name, value)).join(" ");
|
|
370
|
+
}
|
|
371
|
+
function generateTonalPaletteVars(paletteName, palette, scheme2, applyContrast, adaptiveShades2) {
|
|
372
|
+
return STANDARD_TONES.map((tone) => {
|
|
373
|
+
let toneToUse = tone;
|
|
374
|
+
if (adaptiveShades2 && scheme2.isDark) {
|
|
375
|
+
toneToUse = 100 - tone;
|
|
376
|
+
}
|
|
377
|
+
if (applyContrast) {
|
|
378
|
+
toneToUse = adjustToneForContrast(toneToUse, scheme2.contrastLevel);
|
|
379
|
+
}
|
|
380
|
+
const color = palette.tone(toneToUse);
|
|
381
|
+
return cssVar(`${paletteName}-${tone}`, color);
|
|
382
|
+
}).join(" ");
|
|
383
|
+
}
|
|
384
|
+
function generateTonalVars(s) {
|
|
385
|
+
return Object.entries(allPalettes).map(
|
|
386
|
+
([name, palette]) => generateTonalPaletteVars(
|
|
387
|
+
kebabCase(name),
|
|
388
|
+
palette,
|
|
389
|
+
s,
|
|
390
|
+
contrastAllColors,
|
|
391
|
+
adaptiveShades
|
|
392
|
+
)
|
|
393
|
+
).join(" ");
|
|
394
|
+
}
|
|
395
|
+
const lightVars = toCssVars(mergedColorsLight);
|
|
396
|
+
const darkVars = toCssVars(mergedColorsDark);
|
|
397
|
+
const lightTonalVars = generateTonalVars(lightScheme);
|
|
398
|
+
const darkTonalVars = generateTonalVars(darkScheme);
|
|
399
|
+
return `
|
|
400
|
+
:root { ${lightVars} ${lightTonalVars} }
|
|
401
|
+
.dark { ${darkVars} ${adaptiveShades ? darkTonalVars : lightTonalVars} }
|
|
402
|
+
`;
|
|
403
|
+
},
|
|
404
|
+
//
|
|
405
|
+
// ██ ███████ ██████ ███ ██
|
|
406
|
+
// ██ ██ ██ ██ ████ ██
|
|
407
|
+
// ██ ███████ ██ ██ ██ ██ ██
|
|
408
|
+
// ██ ██ ██ ██ ██ ██ ██ ██
|
|
409
|
+
// █████ ███████ ██████ ██ ████
|
|
410
|
+
//
|
|
411
|
+
toJson() {
|
|
412
|
+
const fixtureTokenOrder = [
|
|
413
|
+
"primary",
|
|
414
|
+
"surfaceTint",
|
|
415
|
+
"onPrimary",
|
|
416
|
+
"primaryContainer",
|
|
417
|
+
"onPrimaryContainer",
|
|
418
|
+
"secondary",
|
|
419
|
+
"onSecondary",
|
|
420
|
+
"secondaryContainer",
|
|
421
|
+
"onSecondaryContainer",
|
|
422
|
+
"tertiary",
|
|
423
|
+
"onTertiary",
|
|
424
|
+
"tertiaryContainer",
|
|
425
|
+
"onTertiaryContainer",
|
|
426
|
+
"error",
|
|
427
|
+
"onError",
|
|
428
|
+
"errorContainer",
|
|
429
|
+
"onErrorContainer",
|
|
430
|
+
"background",
|
|
431
|
+
"onBackground",
|
|
432
|
+
"surface",
|
|
433
|
+
"onSurface",
|
|
434
|
+
"surfaceVariant",
|
|
435
|
+
"onSurfaceVariant",
|
|
436
|
+
"outline",
|
|
437
|
+
"outlineVariant",
|
|
438
|
+
"shadow",
|
|
439
|
+
"scrim",
|
|
440
|
+
"inverseSurface",
|
|
441
|
+
"inverseOnSurface",
|
|
442
|
+
"inversePrimary",
|
|
443
|
+
"primaryFixed",
|
|
444
|
+
"onPrimaryFixed",
|
|
445
|
+
"primaryFixedDim",
|
|
446
|
+
"onPrimaryFixedVariant",
|
|
447
|
+
"secondaryFixed",
|
|
448
|
+
"onSecondaryFixed",
|
|
449
|
+
"secondaryFixedDim",
|
|
450
|
+
"onSecondaryFixedVariant",
|
|
451
|
+
"tertiaryFixed",
|
|
452
|
+
"onTertiaryFixed",
|
|
453
|
+
"tertiaryFixedDim",
|
|
454
|
+
"onTertiaryFixedVariant",
|
|
455
|
+
"surfaceDim",
|
|
456
|
+
"surfaceBright",
|
|
457
|
+
"surfaceContainerLowest",
|
|
458
|
+
"surfaceContainerLow",
|
|
459
|
+
"surfaceContainer",
|
|
460
|
+
"surfaceContainerHigh",
|
|
461
|
+
"surfaceContainerHighest"
|
|
462
|
+
];
|
|
463
|
+
const neuHct = neutral ? Hct.fromInt(argbFromHex(neutral)) : sourceHct;
|
|
464
|
+
const nvHct = neutralVariant ? Hct.fromInt(argbFromHex(neutralVariant)) : sourceHct;
|
|
465
|
+
const rawPalettes = {
|
|
466
|
+
primary: TonalPalette.fromInt(effectiveSourceArgb),
|
|
467
|
+
secondary: secondary ? TonalPalette.fromInt(argbFromHex(secondary)) : TonalPalette.fromHueAndChroma(sourceHct.hue, sourceHct.chroma / 3),
|
|
468
|
+
tertiary: tertiary ? TonalPalette.fromInt(argbFromHex(tertiary)) : TonalPalette.fromHueAndChroma(
|
|
469
|
+
(sourceHct.hue + 60) % 360,
|
|
470
|
+
sourceHct.chroma / 2
|
|
471
|
+
),
|
|
472
|
+
neutral: TonalPalette.fromHueAndChroma(
|
|
473
|
+
neuHct.hue,
|
|
474
|
+
Math.min(neuHct.chroma / 12, 4)
|
|
475
|
+
),
|
|
476
|
+
"neutral-variant": TonalPalette.fromHueAndChroma(
|
|
477
|
+
nvHct.hue,
|
|
478
|
+
Math.min(nvHct.chroma / 6, 8)
|
|
479
|
+
)
|
|
480
|
+
};
|
|
481
|
+
function buildJsonSchemes() {
|
|
482
|
+
function extractSchemeColors(scheme2, backgroundScheme) {
|
|
483
|
+
const colors = {};
|
|
484
|
+
for (const tokenName of fixtureTokenOrder) {
|
|
485
|
+
const dynamicColor = MaterialDynamicColors[tokenName];
|
|
486
|
+
const useScheme = backgroundScheme && (tokenName === "background" || tokenName === "onBackground") ? backgroundScheme : scheme2;
|
|
487
|
+
colors[tokenName] = hexFromArgb(
|
|
488
|
+
dynamicColor.getArgb(useScheme)
|
|
489
|
+
).toUpperCase();
|
|
490
|
+
}
|
|
491
|
+
return colors;
|
|
492
|
+
}
|
|
493
|
+
function resolveOverridePalette(hex, role) {
|
|
494
|
+
if (!hex) return null;
|
|
495
|
+
return new SchemeClass(Hct.fromInt(argbFromHex(hex)), false, 0)[role];
|
|
496
|
+
}
|
|
497
|
+
const secPalette = resolveOverridePalette(secondary, "primaryPalette");
|
|
498
|
+
const terPalette = resolveOverridePalette(tertiary, "primaryPalette");
|
|
499
|
+
const errPalette = resolveOverridePalette(error, "primaryPalette");
|
|
500
|
+
const neuPalette = resolveOverridePalette(neutral, "neutralPalette");
|
|
501
|
+
const nvPalette = resolveOverridePalette(
|
|
502
|
+
neutralVariant,
|
|
503
|
+
"neutralVariantPalette"
|
|
504
|
+
);
|
|
505
|
+
const jsonSchemes = {};
|
|
506
|
+
const jsonContrastLevels = [
|
|
507
|
+
{ name: "light", isDark: false, contrast: 0 },
|
|
508
|
+
{ name: "light-medium-contrast", isDark: false, contrast: 0.5 },
|
|
509
|
+
{ name: "light-high-contrast", isDark: false, contrast: 1 },
|
|
510
|
+
{ name: "dark", isDark: true, contrast: 0 },
|
|
511
|
+
{ name: "dark-medium-contrast", isDark: true, contrast: 0.5 },
|
|
512
|
+
{ name: "dark-high-contrast", isDark: true, contrast: 1 }
|
|
513
|
+
];
|
|
514
|
+
for (const { name, isDark, contrast: contrast2 } of jsonContrastLevels) {
|
|
515
|
+
const baseScheme2 = new SchemeClass(primaryHct, isDark, contrast2);
|
|
516
|
+
const composedScheme = new DynamicScheme({
|
|
517
|
+
sourceColorArgb: effectiveSourceArgb,
|
|
518
|
+
variant: schemeToVariant[scheme],
|
|
519
|
+
contrastLevel: contrast2,
|
|
520
|
+
isDark,
|
|
521
|
+
primaryPalette: baseScheme2.primaryPalette,
|
|
522
|
+
secondaryPalette: secPalette || baseScheme2.secondaryPalette,
|
|
523
|
+
tertiaryPalette: terPalette || baseScheme2.tertiaryPalette,
|
|
524
|
+
neutralPalette: neuPalette || baseScheme2.neutralPalette,
|
|
525
|
+
neutralVariantPalette: nvPalette || baseScheme2.neutralVariantPalette
|
|
526
|
+
});
|
|
527
|
+
if (errPalette) composedScheme.errorPalette = errPalette;
|
|
528
|
+
jsonSchemes[name] = extractSchemeColors(composedScheme, baseScheme2);
|
|
529
|
+
}
|
|
530
|
+
return jsonSchemes;
|
|
531
|
+
}
|
|
532
|
+
function rawPalettesToJson() {
|
|
533
|
+
const jsonPalettes = {};
|
|
534
|
+
const RAW_PALETTE_NAMES = [
|
|
535
|
+
"primary",
|
|
536
|
+
"secondary",
|
|
537
|
+
"tertiary",
|
|
538
|
+
"neutral",
|
|
539
|
+
"neutral-variant"
|
|
540
|
+
];
|
|
541
|
+
for (const name of RAW_PALETTE_NAMES) {
|
|
542
|
+
const palette = rawPalettes[name];
|
|
543
|
+
const tones = {};
|
|
544
|
+
for (const tone of STANDARD_TONES) {
|
|
545
|
+
tones[tone.toString()] = hexFromArgb(
|
|
546
|
+
palette.tone(tone)
|
|
547
|
+
).toUpperCase();
|
|
548
|
+
}
|
|
549
|
+
jsonPalettes[name] = tones;
|
|
550
|
+
}
|
|
551
|
+
return jsonPalettes;
|
|
552
|
+
}
|
|
553
|
+
function buildCoreColors(opts) {
|
|
554
|
+
const colors = { primary: opts.primary };
|
|
555
|
+
if (opts.secondary) colors.secondary = opts.secondary.toUpperCase();
|
|
556
|
+
if (opts.tertiary) colors.tertiary = opts.tertiary.toUpperCase();
|
|
557
|
+
if (opts.error) colors.error = opts.error.toUpperCase();
|
|
558
|
+
if (opts.neutral) colors.neutral = opts.neutral.toUpperCase();
|
|
559
|
+
if (opts.neutralVariant)
|
|
560
|
+
colors.neutralVariant = opts.neutralVariant.toUpperCase();
|
|
561
|
+
return colors;
|
|
562
|
+
}
|
|
563
|
+
const seed = hexSource.toUpperCase();
|
|
564
|
+
const coreColors = buildCoreColors({
|
|
565
|
+
primary: (primary || hexSource).toUpperCase(),
|
|
566
|
+
secondary,
|
|
567
|
+
tertiary,
|
|
568
|
+
error,
|
|
569
|
+
neutral,
|
|
570
|
+
neutralVariant
|
|
571
|
+
});
|
|
572
|
+
const extendedColors = hexCustomColors.map((c) => ({
|
|
573
|
+
name: c.name,
|
|
574
|
+
color: c.hex.toUpperCase(),
|
|
575
|
+
description: "",
|
|
576
|
+
harmonized: c.blend ?? DEFAULT_BLEND
|
|
577
|
+
}));
|
|
578
|
+
return {
|
|
579
|
+
seed,
|
|
580
|
+
coreColors,
|
|
581
|
+
extendedColors,
|
|
582
|
+
schemes: buildJsonSchemes(),
|
|
583
|
+
palettes: rawPalettesToJson()
|
|
584
|
+
};
|
|
585
|
+
},
|
|
586
|
+
//
|
|
587
|
+
// ███████ ██ ██████ ███ ███ █████
|
|
588
|
+
// ██ ██ ██ ████ ████ ██ ██
|
|
589
|
+
// █████ ██ ██ ███ ██ ████ ██ ███████
|
|
590
|
+
// ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
591
|
+
// ██ ██ ██████ ██ ██ ██ ██
|
|
592
|
+
//
|
|
593
|
+
toFigmaTokens() {
|
|
594
|
+
function argbToFigmaColorValue(argb) {
|
|
595
|
+
return {
|
|
596
|
+
colorSpace: "srgb",
|
|
597
|
+
components: [
|
|
598
|
+
redFromArgb(argb) / 255,
|
|
599
|
+
greenFromArgb(argb) / 255,
|
|
600
|
+
blueFromArgb(argb) / 255
|
|
601
|
+
],
|
|
602
|
+
alpha: 1,
|
|
603
|
+
hex: hexFromArgb(argb).toUpperCase()
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function figmaToken(argb) {
|
|
607
|
+
return {
|
|
608
|
+
$type: "color",
|
|
609
|
+
$value: argbToFigmaColorValue(argb),
|
|
610
|
+
$extensions: {
|
|
611
|
+
"com.figma.scopes": ["ALL_SCOPES"],
|
|
612
|
+
"com.figma.isOverride": true
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function buildFigmaSchemeTokens(mergedColors) {
|
|
617
|
+
const tokens = {};
|
|
618
|
+
for (const [name, argb] of Object.entries(mergedColors)) {
|
|
619
|
+
tokens[startCase(name)] = figmaToken(argb);
|
|
620
|
+
}
|
|
621
|
+
return tokens;
|
|
622
|
+
}
|
|
623
|
+
function buildFigmaPaletteTokens(isDark) {
|
|
624
|
+
const palettes = {};
|
|
625
|
+
for (const [name, palette] of Object.entries(allPalettes)) {
|
|
626
|
+
const tones = {};
|
|
627
|
+
for (const tone of STANDARD_TONES) {
|
|
628
|
+
let toneToUse = tone;
|
|
629
|
+
if (adaptiveShades && isDark) {
|
|
630
|
+
toneToUse = 100 - tone;
|
|
631
|
+
}
|
|
632
|
+
if (contrastAllColors) {
|
|
633
|
+
toneToUse = adjustToneForContrast(toneToUse, contrast);
|
|
634
|
+
}
|
|
635
|
+
const argb = palette.tone(toneToUse);
|
|
636
|
+
tones[tone.toString()] = figmaToken(argb);
|
|
637
|
+
}
|
|
638
|
+
palettes[startCase(name)] = tones;
|
|
639
|
+
}
|
|
640
|
+
return palettes;
|
|
641
|
+
}
|
|
642
|
+
function buildModeFile(modeName, mergedColors, isDark) {
|
|
643
|
+
return {
|
|
644
|
+
Schemes: buildFigmaSchemeTokens(mergedColors),
|
|
645
|
+
Palettes: buildFigmaPaletteTokens(isDark),
|
|
646
|
+
$extensions: {
|
|
647
|
+
"com.figma.modeName": modeName
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
"Light.tokens.json": buildModeFile("Light", mergedColorsLight, false),
|
|
653
|
+
"Dark.tokens.json": buildModeFile("Dark", mergedColorsDark, true)
|
|
654
|
+
};
|
|
655
|
+
},
|
|
656
|
+
//
|
|
657
|
+
// API
|
|
658
|
+
//
|
|
659
|
+
mergedColorsLight,
|
|
660
|
+
mergedColorsDark,
|
|
661
|
+
allPalettes
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/cli.ts
|
|
666
|
+
var program = new Command();
|
|
667
|
+
program.name("react-mcu").description("m3 color system for react");
|
|
668
|
+
program.command("builder").description("Generate a color theme from a source color").argument("<source>", "Source color in hex format (e.g. #6750A4)").addOption(
|
|
669
|
+
new Option("--scheme <name>", "Color scheme variant").choices(schemeNames).default(DEFAULT_SCHEME)
|
|
670
|
+
).option(
|
|
671
|
+
"--contrast <number>",
|
|
672
|
+
"Contrast level from -1.0 to 1.0",
|
|
673
|
+
parseFloat,
|
|
674
|
+
DEFAULT_CONTRAST
|
|
675
|
+
).option("--primary <hex>", "Primary color override").option("--secondary <hex>", "Secondary color override").option("--tertiary <hex>", "Tertiary color override").option("--error <hex>", "Error color override").option("--neutral <hex>", "Neutral color override").option("--neutral-variant <hex>", "Neutral variant color override").option(
|
|
676
|
+
"--custom-colors <json>",
|
|
677
|
+
`Custom colors as JSON array (e.g. '[{"name":"brand","hex":"#FF5733","blend":true}]')`
|
|
678
|
+
).option("--format <type>", "Output format: json, css, or figma", "figma").option("--output <dir>", "Output directory (required for figma format)").option(
|
|
679
|
+
"--adaptive-shades",
|
|
680
|
+
"Adapt tonal palette shades for dark mode",
|
|
681
|
+
DEFAULT_ADAPTIVE_SHADES
|
|
682
|
+
).option(
|
|
683
|
+
"--contrast-all-colors",
|
|
684
|
+
"Apply contrast adjustment to tonal palette shades",
|
|
685
|
+
DEFAULT_CONTRAST_ALL_COLORS
|
|
686
|
+
).action((source, opts) => {
|
|
687
|
+
let customColors = [];
|
|
688
|
+
if (opts.customColors) {
|
|
689
|
+
try {
|
|
690
|
+
const parsed = JSON.parse(opts.customColors);
|
|
691
|
+
customColors = parsed.map((c) => ({
|
|
692
|
+
name: c.name,
|
|
693
|
+
hex: c.hex,
|
|
694
|
+
blend: c.blend ?? DEFAULT_BLEND
|
|
695
|
+
}));
|
|
696
|
+
} catch {
|
|
697
|
+
console.error("Error: --custom-colors must be valid JSON");
|
|
698
|
+
process.exit(1);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const result = builder(source, {
|
|
702
|
+
scheme: opts.scheme,
|
|
703
|
+
contrast: opts.contrast,
|
|
704
|
+
primary: opts.primary,
|
|
705
|
+
secondary: opts.secondary,
|
|
706
|
+
tertiary: opts.tertiary,
|
|
707
|
+
error: opts.error,
|
|
708
|
+
neutral: opts.neutral,
|
|
709
|
+
neutralVariant: opts.neutralVariant,
|
|
710
|
+
customColors,
|
|
711
|
+
adaptiveShades: opts.adaptiveShades,
|
|
712
|
+
contrastAllColors: opts.contrastAllColors
|
|
713
|
+
});
|
|
714
|
+
if (opts.format === "css") {
|
|
715
|
+
process.stdout.write(result.toCss());
|
|
716
|
+
} else if (opts.format === "figma") {
|
|
717
|
+
const outputDir = opts.output ?? "mcu-theme";
|
|
718
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
719
|
+
const files = result.toFigmaTokens();
|
|
720
|
+
for (const [filename, content] of Object.entries(files)) {
|
|
721
|
+
const filePath = path.join(outputDir, filename);
|
|
722
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + "\n");
|
|
723
|
+
console.error(`wrote ${filePath}`);
|
|
724
|
+
}
|
|
725
|
+
} else {
|
|
726
|
+
process.stdout.write(JSON.stringify(result.toJson(), null, 2) + "\n");
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
program.parse();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import { CustomColor, TonalPalette } from '@material/material-color-utilities';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
type HexCustomColor = Omit<CustomColor, "value"> & {
|
|
5
5
|
hex: string;
|
|
@@ -34,39 +34,26 @@ type McuConfig = {
|
|
|
34
34
|
colorMatch?: boolean;
|
|
35
35
|
/**
|
|
36
36
|
* Array of custom colors to include in the generated palette.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* customColors={[
|
|
41
|
-
* { name: "brand", hex: "#FF5733", blend: true },
|
|
42
|
-
* { name: "success", hex: "#28A745", blend: false }
|
|
43
|
-
* ]}
|
|
44
|
-
* ```
|
|
37
|
+
* Each custom color can be blended with the source color for harmonization.
|
|
45
38
|
*/
|
|
46
39
|
customColors?: HexCustomColor[];
|
|
47
40
|
/**
|
|
48
|
-
*
|
|
49
|
-
* When
|
|
41
|
+
* Apply contrast adjustment to tonal palette shades when true.
|
|
42
|
+
* When true, all tonal palette shades are adjusted based on the contrast level.
|
|
43
|
+
* When false (default), contrast adjustments only apply to core Material Design tokens.
|
|
50
44
|
*/
|
|
51
45
|
contrastAllColors?: boolean;
|
|
52
46
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
47
|
+
* Adapt tonal palette shades for dark mode.
|
|
48
|
+
* When true, tonal palette shades automatically invert for dark mode
|
|
49
|
+
* (high values become dark, low values become light).
|
|
50
|
+
* When false (default), tonal palette values remain constant across light/dark mode.
|
|
56
51
|
*/
|
|
57
52
|
adaptiveShades?: boolean;
|
|
58
53
|
};
|
|
59
54
|
declare const schemeNames: readonly ["tonalSpot", "monochrome", "neutral", "vibrant", "expressive", "fidelity", "content"];
|
|
60
55
|
type SchemeName = (typeof schemeNames)[number];
|
|
61
|
-
declare const
|
|
62
|
-
declare const DEFAULT_CONTRAST = 0;
|
|
63
|
-
declare const DEFAULT_CONTRAST_ALL_COLORS = false;
|
|
64
|
-
declare const DEFAULT_ADAPTIVE_SHADES = false;
|
|
65
|
-
declare const DEFAULT_BLEND = true;
|
|
66
|
-
declare function Mcu({ source, scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, colorMatch, customColors, contrastAllColors, adaptiveShades, children, }: McuConfig & {
|
|
67
|
-
children?: React.ReactNode;
|
|
68
|
-
}): react_jsx_runtime.JSX.Element;
|
|
69
|
-
declare const tokenNames: readonly ["background", "onBackground", "surface", "surfaceDim", "surfaceBright", "surfaceContainerLowest", "surfaceContainerLow", "surfaceContainer", "surfaceContainerHigh", "surfaceContainerHighest", "onSurface", "surfaceVariant", "onSurfaceVariant", "outline", "outlineVariant", "inverseSurface", "inverseOnSurface", "primary", "surfaceTint", "onPrimary", "primaryContainer", "onPrimaryContainer", "primaryFixed", "primaryFixedDim", "onPrimaryFixed", "onPrimaryFixedVariant", "inversePrimary", "secondary", "onSecondary", "secondaryContainer", "onSecondaryContainer", "secondaryFixed", "secondaryFixedDim", "onSecondaryFixed", "onSecondaryFixedVariant", "tertiary", "onTertiary", "tertiaryContainer", "onTertiaryContainer", "tertiaryFixed", "tertiaryFixedDim", "onTertiaryFixed", "onTertiaryFixedVariant", "error", "onError", "errorContainer", "onErrorContainer", "scrim", "shadow"];
|
|
56
|
+
declare const tokenNames: readonly ["background", "error", "errorContainer", "inverseOnSurface", "inversePrimary", "inverseSurface", "onBackground", "onError", "onErrorContainer", "onPrimary", "onPrimaryContainer", "onPrimaryFixed", "onPrimaryFixedVariant", "onSecondary", "onSecondaryContainer", "onSecondaryFixed", "onSecondaryFixedVariant", "onSurface", "onSurfaceVariant", "onTertiary", "onTertiaryContainer", "onTertiaryFixed", "onTertiaryFixedVariant", "outline", "outlineVariant", "primary", "primaryContainer", "primaryFixed", "primaryFixedDim", "scrim", "secondary", "secondaryContainer", "secondaryFixed", "secondaryFixedDim", "shadow", "surface", "surfaceBright", "surfaceContainer", "surfaceContainerHigh", "surfaceContainerHighest", "surfaceContainerLow", "surfaceContainerLowest", "surfaceDim", "surfaceTint", "surfaceVariant", "tertiary", "tertiaryContainer", "tertiaryFixed", "tertiaryFixedDim"];
|
|
70
57
|
type TokenName = (typeof tokenNames)[number];
|
|
71
58
|
declare function builder(hexSource: McuConfig["source"], { scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, customColors: hexCustomColors, contrastAllColors, adaptiveShades, }?: Omit<McuConfig, "source">): {
|
|
72
59
|
toCss(): string;
|
|
@@ -162,6 +149,10 @@ declare function builder(hexSource: McuConfig["source"], { scheme, contrast, pri
|
|
|
162
149
|
};
|
|
163
150
|
};
|
|
164
151
|
|
|
152
|
+
declare function Mcu({ source, scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, colorMatch, customColors, contrastAllColors, adaptiveShades, children, }: McuConfig & {
|
|
153
|
+
children?: React.ReactNode;
|
|
154
|
+
}): react_jsx_runtime.JSX.Element;
|
|
155
|
+
|
|
165
156
|
type Api = {
|
|
166
157
|
initials: McuConfig;
|
|
167
158
|
setMcuConfig: (config: McuConfig) => void;
|
|
@@ -170,4 +161,4 @@ type Api = {
|
|
|
170
161
|
};
|
|
171
162
|
declare const useMcu: () => Api;
|
|
172
163
|
|
|
173
|
-
export {
|
|
164
|
+
export { Mcu, builder, useMcu };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
// src/
|
|
3
|
+
// src/lib/builder.ts
|
|
4
4
|
import {
|
|
5
5
|
argbFromHex,
|
|
6
6
|
Blend,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
DynamicScheme,
|
|
10
10
|
greenFromArgb,
|
|
11
11
|
Hct,
|
|
12
|
-
hexFromArgb
|
|
12
|
+
hexFromArgb,
|
|
13
13
|
MaterialDynamicColors,
|
|
14
14
|
redFromArgb,
|
|
15
15
|
SchemeContent,
|
|
@@ -22,85 +22,6 @@ import {
|
|
|
22
22
|
TonalPalette
|
|
23
23
|
} from "@material/material-color-utilities";
|
|
24
24
|
import { kebabCase, startCase, upperFirst } from "lodash-es";
|
|
25
|
-
import { useMemo as useMemo2 } from "react";
|
|
26
|
-
|
|
27
|
-
// src/Mcu.context.tsx
|
|
28
|
-
import {
|
|
29
|
-
hexFromArgb
|
|
30
|
-
} from "@material/material-color-utilities";
|
|
31
|
-
import React, {
|
|
32
|
-
useCallback,
|
|
33
|
-
useInsertionEffect,
|
|
34
|
-
useMemo,
|
|
35
|
-
useState
|
|
36
|
-
} from "react";
|
|
37
|
-
|
|
38
|
-
// src/lib/createRequiredContext.ts
|
|
39
|
-
import { createContext, useContext } from "react";
|
|
40
|
-
var createRequiredContext = () => {
|
|
41
|
-
const Ctx = createContext(null);
|
|
42
|
-
const useCtx = () => {
|
|
43
|
-
const contextValue = useContext(Ctx);
|
|
44
|
-
if (contextValue === null) {
|
|
45
|
-
throw new Error("Context value is null");
|
|
46
|
-
}
|
|
47
|
-
return contextValue;
|
|
48
|
-
};
|
|
49
|
-
return [useCtx, Ctx.Provider, Ctx];
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// src/Mcu.context.tsx
|
|
53
|
-
import { jsx } from "react/jsx-runtime";
|
|
54
|
-
var [useMcu, Provider, McuContext] = createRequiredContext();
|
|
55
|
-
var McuProvider = ({
|
|
56
|
-
styleId,
|
|
57
|
-
children,
|
|
58
|
-
...configProps
|
|
59
|
-
}) => {
|
|
60
|
-
const [initials] = useState(() => configProps);
|
|
61
|
-
const [mcuConfig, setMcuConfig] = useState(initials);
|
|
62
|
-
const configKey = JSON.stringify(configProps);
|
|
63
|
-
React.useEffect(() => {
|
|
64
|
-
setMcuConfig(configProps);
|
|
65
|
-
}, [configKey]);
|
|
66
|
-
const { css, mergedColorsLight, mergedColorsDark, allPalettes } = useMemo(() => {
|
|
67
|
-
const { toCss, ...rest } = builder(mcuConfig.source, mcuConfig);
|
|
68
|
-
return { css: toCss(), ...rest };
|
|
69
|
-
}, [mcuConfig]);
|
|
70
|
-
useInsertionEffect(() => {
|
|
71
|
-
let tag = document.getElementById(styleId);
|
|
72
|
-
if (!tag) {
|
|
73
|
-
tag = document.createElement("style");
|
|
74
|
-
tag.id = styleId;
|
|
75
|
-
document.head.appendChild(tag);
|
|
76
|
-
}
|
|
77
|
-
tag.textContent = css;
|
|
78
|
-
}, [css, styleId]);
|
|
79
|
-
const getMcuColor = useCallback(
|
|
80
|
-
(colorName, theme) => {
|
|
81
|
-
const mergedColors = theme === "light" ? mergedColorsLight : mergedColorsDark;
|
|
82
|
-
const colorValue = mergedColors[colorName];
|
|
83
|
-
if (colorValue === void 0) {
|
|
84
|
-
throw new Error(`Unknown MCU token '${colorName}'`);
|
|
85
|
-
}
|
|
86
|
-
return hexFromArgb(colorValue);
|
|
87
|
-
},
|
|
88
|
-
[mergedColorsDark, mergedColorsLight]
|
|
89
|
-
);
|
|
90
|
-
const value = useMemo(
|
|
91
|
-
() => ({
|
|
92
|
-
initials,
|
|
93
|
-
setMcuConfig,
|
|
94
|
-
getMcuColor,
|
|
95
|
-
allPalettes
|
|
96
|
-
}),
|
|
97
|
-
[getMcuColor, initials, allPalettes]
|
|
98
|
-
);
|
|
99
|
-
return /* @__PURE__ */ jsx(Provider, { value, children });
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// src/Mcu.tsx
|
|
103
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
104
25
|
function adjustToneForContrast(baseTone, contrastLevel, adjustmentFactor = DEFAULT_CONTRAST_ADJUSTMENT_FACTOR) {
|
|
105
26
|
if (contrastLevel === 0) return baseTone;
|
|
106
27
|
const distanceToCenter = baseTone - 50;
|
|
@@ -108,15 +29,6 @@ function adjustToneForContrast(baseTone, contrastLevel, adjustmentFactor = DEFAU
|
|
|
108
29
|
const adjustedTone = baseTone + delta;
|
|
109
30
|
return Math.max(0, Math.min(100, adjustedTone));
|
|
110
31
|
}
|
|
111
|
-
var schemeNames = [
|
|
112
|
-
"tonalSpot",
|
|
113
|
-
"monochrome",
|
|
114
|
-
"neutral",
|
|
115
|
-
"vibrant",
|
|
116
|
-
"expressive",
|
|
117
|
-
"fidelity",
|
|
118
|
-
"content"
|
|
119
|
-
];
|
|
120
32
|
var schemesMap = {
|
|
121
33
|
tonalSpot: SchemeTonalSpot,
|
|
122
34
|
monochrome: SchemeMonochrome,
|
|
@@ -128,7 +40,6 @@ var schemesMap = {
|
|
|
128
40
|
};
|
|
129
41
|
var DEFAULT_SCHEME = "tonalSpot";
|
|
130
42
|
var DEFAULT_CONTRAST = 0;
|
|
131
|
-
var DEFAULT_COLOR_MATCH = false;
|
|
132
43
|
var DEFAULT_CUSTOM_COLORS = [];
|
|
133
44
|
var DEFAULT_CONTRAST_ALL_COLORS = false;
|
|
134
45
|
var DEFAULT_ADAPTIVE_SHADES = false;
|
|
@@ -166,116 +77,64 @@ var Variant = {
|
|
|
166
77
|
FRUIT_SALAD: 8
|
|
167
78
|
};
|
|
168
79
|
var schemeToVariant = {
|
|
169
|
-
tonalSpot: Variant.TONAL_SPOT,
|
|
170
80
|
monochrome: Variant.MONOCHROME,
|
|
171
81
|
neutral: Variant.NEUTRAL,
|
|
82
|
+
tonalSpot: Variant.TONAL_SPOT,
|
|
172
83
|
vibrant: Variant.VIBRANT,
|
|
173
84
|
expressive: Variant.EXPRESSIVE,
|
|
174
85
|
fidelity: Variant.FIDELITY,
|
|
175
86
|
content: Variant.CONTENT
|
|
176
87
|
};
|
|
177
|
-
var mcuStyleId = "mcu-styles";
|
|
178
|
-
function Mcu({
|
|
179
|
-
source,
|
|
180
|
-
scheme = DEFAULT_SCHEME,
|
|
181
|
-
contrast = DEFAULT_CONTRAST,
|
|
182
|
-
primary,
|
|
183
|
-
secondary,
|
|
184
|
-
tertiary,
|
|
185
|
-
neutral,
|
|
186
|
-
neutralVariant,
|
|
187
|
-
error,
|
|
188
|
-
colorMatch = DEFAULT_COLOR_MATCH,
|
|
189
|
-
customColors = DEFAULT_CUSTOM_COLORS,
|
|
190
|
-
contrastAllColors = DEFAULT_CONTRAST_ALL_COLORS,
|
|
191
|
-
adaptiveShades = DEFAULT_ADAPTIVE_SHADES,
|
|
192
|
-
children
|
|
193
|
-
}) {
|
|
194
|
-
const config = useMemo2(
|
|
195
|
-
() => ({
|
|
196
|
-
source,
|
|
197
|
-
scheme,
|
|
198
|
-
contrast,
|
|
199
|
-
primary,
|
|
200
|
-
secondary,
|
|
201
|
-
tertiary,
|
|
202
|
-
neutral,
|
|
203
|
-
neutralVariant,
|
|
204
|
-
error,
|
|
205
|
-
colorMatch,
|
|
206
|
-
customColors,
|
|
207
|
-
// extras features
|
|
208
|
-
contrastAllColors,
|
|
209
|
-
adaptiveShades
|
|
210
|
-
}),
|
|
211
|
-
[
|
|
212
|
-
contrast,
|
|
213
|
-
customColors,
|
|
214
|
-
scheme,
|
|
215
|
-
source,
|
|
216
|
-
primary,
|
|
217
|
-
secondary,
|
|
218
|
-
tertiary,
|
|
219
|
-
neutral,
|
|
220
|
-
neutralVariant,
|
|
221
|
-
error,
|
|
222
|
-
colorMatch,
|
|
223
|
-
contrastAllColors,
|
|
224
|
-
adaptiveShades
|
|
225
|
-
]
|
|
226
|
-
);
|
|
227
|
-
return /* @__PURE__ */ jsx2(McuProvider, { ...config, styleId: mcuStyleId, children });
|
|
228
|
-
}
|
|
229
88
|
var tokenNames = [
|
|
230
89
|
"background",
|
|
90
|
+
"error",
|
|
91
|
+
"errorContainer",
|
|
92
|
+
"inverseOnSurface",
|
|
93
|
+
"inversePrimary",
|
|
94
|
+
"inverseSurface",
|
|
231
95
|
"onBackground",
|
|
232
|
-
"
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
"
|
|
237
|
-
"
|
|
238
|
-
"
|
|
239
|
-
"
|
|
96
|
+
"onError",
|
|
97
|
+
"onErrorContainer",
|
|
98
|
+
"onPrimary",
|
|
99
|
+
"onPrimaryContainer",
|
|
100
|
+
"onPrimaryFixed",
|
|
101
|
+
"onPrimaryFixedVariant",
|
|
102
|
+
"onSecondary",
|
|
103
|
+
"onSecondaryContainer",
|
|
104
|
+
"onSecondaryFixed",
|
|
105
|
+
"onSecondaryFixedVariant",
|
|
240
106
|
"onSurface",
|
|
241
|
-
"surfaceVariant",
|
|
242
107
|
"onSurfaceVariant",
|
|
108
|
+
"onTertiary",
|
|
109
|
+
"onTertiaryContainer",
|
|
110
|
+
"onTertiaryFixed",
|
|
111
|
+
"onTertiaryFixedVariant",
|
|
243
112
|
"outline",
|
|
244
113
|
"outlineVariant",
|
|
245
|
-
"inverseSurface",
|
|
246
|
-
"inverseOnSurface",
|
|
247
114
|
"primary",
|
|
248
|
-
"surfaceTint",
|
|
249
|
-
"onPrimary",
|
|
250
115
|
"primaryContainer",
|
|
251
|
-
"onPrimaryContainer",
|
|
252
116
|
"primaryFixed",
|
|
253
117
|
"primaryFixedDim",
|
|
254
|
-
"
|
|
255
|
-
"onPrimaryFixedVariant",
|
|
256
|
-
"inversePrimary",
|
|
118
|
+
"scrim",
|
|
257
119
|
"secondary",
|
|
258
|
-
"onSecondary",
|
|
259
120
|
"secondaryContainer",
|
|
260
|
-
"onSecondaryContainer",
|
|
261
121
|
"secondaryFixed",
|
|
262
122
|
"secondaryFixedDim",
|
|
263
|
-
"
|
|
264
|
-
"
|
|
123
|
+
"shadow",
|
|
124
|
+
"surface",
|
|
125
|
+
"surfaceBright",
|
|
126
|
+
"surfaceContainer",
|
|
127
|
+
"surfaceContainerHigh",
|
|
128
|
+
"surfaceContainerHighest",
|
|
129
|
+
"surfaceContainerLow",
|
|
130
|
+
"surfaceContainerLowest",
|
|
131
|
+
"surfaceDim",
|
|
132
|
+
"surfaceTint",
|
|
133
|
+
"surfaceVariant",
|
|
265
134
|
"tertiary",
|
|
266
|
-
"onTertiary",
|
|
267
135
|
"tertiaryContainer",
|
|
268
|
-
"onTertiaryContainer",
|
|
269
136
|
"tertiaryFixed",
|
|
270
|
-
"tertiaryFixedDim"
|
|
271
|
-
"onTertiaryFixed",
|
|
272
|
-
"onTertiaryFixedVariant",
|
|
273
|
-
"error",
|
|
274
|
-
"onError",
|
|
275
|
-
"errorContainer",
|
|
276
|
-
"onErrorContainer",
|
|
277
|
-
"scrim",
|
|
278
|
-
"shadow"
|
|
137
|
+
"tertiaryFixedDim"
|
|
279
138
|
];
|
|
280
139
|
function toRecord(arr, getEntry) {
|
|
281
140
|
return Object.fromEntries(arr.map(getEntry));
|
|
@@ -489,7 +348,7 @@ function builder(hexSource, {
|
|
|
489
348
|
toCss() {
|
|
490
349
|
function cssVar(colorName, colorValue) {
|
|
491
350
|
const name = `--mcu-${kebabCase(colorName)}`;
|
|
492
|
-
const value =
|
|
351
|
+
const value = hexFromArgb(colorValue);
|
|
493
352
|
return `${name}:${value};`;
|
|
494
353
|
}
|
|
495
354
|
function toCssVars(mergedColors) {
|
|
@@ -611,7 +470,7 @@ function builder(hexSource, {
|
|
|
611
470
|
for (const tokenName of fixtureTokenOrder) {
|
|
612
471
|
const dynamicColor = MaterialDynamicColors[tokenName];
|
|
613
472
|
const useScheme = backgroundScheme && (tokenName === "background" || tokenName === "onBackground") ? backgroundScheme : scheme2;
|
|
614
|
-
colors[tokenName] =
|
|
473
|
+
colors[tokenName] = hexFromArgb(
|
|
615
474
|
dynamicColor.getArgb(useScheme)
|
|
616
475
|
).toUpperCase();
|
|
617
476
|
}
|
|
@@ -669,7 +528,7 @@ function builder(hexSource, {
|
|
|
669
528
|
const palette = rawPalettes[name];
|
|
670
529
|
const tones = {};
|
|
671
530
|
for (const tone of STANDARD_TONES) {
|
|
672
|
-
tones[tone.toString()] =
|
|
531
|
+
tones[tone.toString()] = hexFromArgb(
|
|
673
532
|
palette.tone(tone)
|
|
674
533
|
).toUpperCase();
|
|
675
534
|
}
|
|
@@ -727,7 +586,7 @@ function builder(hexSource, {
|
|
|
727
586
|
blueFromArgb(argb) / 255
|
|
728
587
|
],
|
|
729
588
|
alpha: 1,
|
|
730
|
-
hex:
|
|
589
|
+
hex: hexFromArgb(argb).toUpperCase()
|
|
731
590
|
};
|
|
732
591
|
}
|
|
733
592
|
function figmaToken(argb) {
|
|
@@ -788,14 +647,142 @@ function builder(hexSource, {
|
|
|
788
647
|
allPalettes
|
|
789
648
|
};
|
|
790
649
|
}
|
|
650
|
+
|
|
651
|
+
// src/Mcu.tsx
|
|
652
|
+
import { useMemo as useMemo2 } from "react";
|
|
653
|
+
|
|
654
|
+
// src/Mcu.context.tsx
|
|
655
|
+
import {
|
|
656
|
+
hexFromArgb as hexFromArgb2
|
|
657
|
+
} from "@material/material-color-utilities";
|
|
658
|
+
import React, {
|
|
659
|
+
useCallback,
|
|
660
|
+
useInsertionEffect,
|
|
661
|
+
useMemo,
|
|
662
|
+
useState
|
|
663
|
+
} from "react";
|
|
664
|
+
|
|
665
|
+
// src/lib/createRequiredContext.ts
|
|
666
|
+
import { createContext, useContext } from "react";
|
|
667
|
+
var createRequiredContext = () => {
|
|
668
|
+
const Ctx = createContext(null);
|
|
669
|
+
const useCtx = () => {
|
|
670
|
+
const contextValue = useContext(Ctx);
|
|
671
|
+
if (contextValue === null) {
|
|
672
|
+
throw new Error("Context value is null");
|
|
673
|
+
}
|
|
674
|
+
return contextValue;
|
|
675
|
+
};
|
|
676
|
+
return [useCtx, Ctx.Provider, Ctx];
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// src/Mcu.context.tsx
|
|
680
|
+
import { jsx } from "react/jsx-runtime";
|
|
681
|
+
var [useMcu, Provider, McuContext] = createRequiredContext();
|
|
682
|
+
var McuProvider = ({
|
|
683
|
+
styleId,
|
|
684
|
+
children,
|
|
685
|
+
...configProps
|
|
686
|
+
}) => {
|
|
687
|
+
const [initials] = useState(() => configProps);
|
|
688
|
+
const [mcuConfig, setMcuConfig] = useState(initials);
|
|
689
|
+
const configKey = JSON.stringify(configProps);
|
|
690
|
+
React.useEffect(() => {
|
|
691
|
+
setMcuConfig(configProps);
|
|
692
|
+
}, [configKey]);
|
|
693
|
+
const { css, mergedColorsLight, mergedColorsDark, allPalettes } = useMemo(() => {
|
|
694
|
+
const { toCss, ...rest } = builder(mcuConfig.source, mcuConfig);
|
|
695
|
+
return { css: toCss(), ...rest };
|
|
696
|
+
}, [mcuConfig]);
|
|
697
|
+
useInsertionEffect(() => {
|
|
698
|
+
let tag = document.getElementById(styleId);
|
|
699
|
+
if (!tag) {
|
|
700
|
+
tag = document.createElement("style");
|
|
701
|
+
tag.id = styleId;
|
|
702
|
+
document.head.appendChild(tag);
|
|
703
|
+
}
|
|
704
|
+
tag.textContent = css;
|
|
705
|
+
}, [css, styleId]);
|
|
706
|
+
const getMcuColor = useCallback(
|
|
707
|
+
(colorName, theme) => {
|
|
708
|
+
const mergedColors = theme === "light" ? mergedColorsLight : mergedColorsDark;
|
|
709
|
+
const colorValue = mergedColors[colorName];
|
|
710
|
+
if (colorValue === void 0) {
|
|
711
|
+
throw new Error(`Unknown MCU token '${colorName}'`);
|
|
712
|
+
}
|
|
713
|
+
return hexFromArgb2(colorValue);
|
|
714
|
+
},
|
|
715
|
+
[mergedColorsDark, mergedColorsLight]
|
|
716
|
+
);
|
|
717
|
+
const value = useMemo(
|
|
718
|
+
() => ({
|
|
719
|
+
initials,
|
|
720
|
+
setMcuConfig,
|
|
721
|
+
getMcuColor,
|
|
722
|
+
allPalettes
|
|
723
|
+
}),
|
|
724
|
+
[getMcuColor, initials, allPalettes]
|
|
725
|
+
);
|
|
726
|
+
return /* @__PURE__ */ jsx(Provider, { value, children });
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// src/Mcu.tsx
|
|
730
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
731
|
+
var mcuStyleId = "mcu-styles";
|
|
732
|
+
var DEFAULT_COLOR_MATCH = false;
|
|
733
|
+
function Mcu({
|
|
734
|
+
source,
|
|
735
|
+
scheme = DEFAULT_SCHEME,
|
|
736
|
+
contrast = DEFAULT_CONTRAST,
|
|
737
|
+
primary,
|
|
738
|
+
secondary,
|
|
739
|
+
tertiary,
|
|
740
|
+
neutral,
|
|
741
|
+
neutralVariant,
|
|
742
|
+
error,
|
|
743
|
+
colorMatch = DEFAULT_COLOR_MATCH,
|
|
744
|
+
customColors = DEFAULT_CUSTOM_COLORS,
|
|
745
|
+
contrastAllColors = DEFAULT_CONTRAST_ALL_COLORS,
|
|
746
|
+
adaptiveShades = DEFAULT_ADAPTIVE_SHADES,
|
|
747
|
+
children
|
|
748
|
+
}) {
|
|
749
|
+
const config = useMemo2(
|
|
750
|
+
() => ({
|
|
751
|
+
source,
|
|
752
|
+
scheme,
|
|
753
|
+
contrast,
|
|
754
|
+
primary,
|
|
755
|
+
secondary,
|
|
756
|
+
tertiary,
|
|
757
|
+
neutral,
|
|
758
|
+
neutralVariant,
|
|
759
|
+
error,
|
|
760
|
+
colorMatch,
|
|
761
|
+
customColors,
|
|
762
|
+
// extras features
|
|
763
|
+
contrastAllColors,
|
|
764
|
+
adaptiveShades
|
|
765
|
+
}),
|
|
766
|
+
[
|
|
767
|
+
contrast,
|
|
768
|
+
customColors,
|
|
769
|
+
scheme,
|
|
770
|
+
source,
|
|
771
|
+
primary,
|
|
772
|
+
secondary,
|
|
773
|
+
tertiary,
|
|
774
|
+
neutral,
|
|
775
|
+
neutralVariant,
|
|
776
|
+
error,
|
|
777
|
+
colorMatch,
|
|
778
|
+
contrastAllColors,
|
|
779
|
+
adaptiveShades
|
|
780
|
+
]
|
|
781
|
+
);
|
|
782
|
+
return /* @__PURE__ */ jsx2(McuProvider, { ...config, styleId: mcuStyleId, children });
|
|
783
|
+
}
|
|
791
784
|
export {
|
|
792
|
-
DEFAULT_ADAPTIVE_SHADES,
|
|
793
|
-
DEFAULT_BLEND,
|
|
794
|
-
DEFAULT_CONTRAST,
|
|
795
|
-
DEFAULT_CONTRAST_ALL_COLORS,
|
|
796
|
-
DEFAULT_SCHEME,
|
|
797
785
|
Mcu,
|
|
798
786
|
builder,
|
|
799
|
-
schemeNames,
|
|
800
787
|
useMcu
|
|
801
788
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-mcu",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "A React component library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"src/tailwind.css"
|
|
30
30
|
],
|
|
31
31
|
"bin": {
|
|
32
|
-
"react-mcu": "./
|
|
32
|
+
"react-mcu": "./dist/cli.js"
|
|
33
33
|
},
|
|
34
34
|
"type": "module",
|
|
35
35
|
"devDependencies": {
|
|
@@ -81,8 +81,7 @@
|
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@material/material-color-utilities": "^0.3.0",
|
|
83
83
|
"commander": "^14.0.3",
|
|
84
|
-
"lodash-es": "^4.17.22"
|
|
85
|
-
"tsx": "^4.21.0"
|
|
84
|
+
"lodash-es": "^4.17.22"
|
|
86
85
|
},
|
|
87
86
|
"scripts": {
|
|
88
87
|
"build": "tsup",
|
package/src/cli.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
// @example
|
|
4
|
-
|
|
5
|
-
// ```sh
|
|
6
|
-
// $ npx tsx src/cli.ts builder '#6750A4'
|
|
7
|
-
// $ npx tsx src/cli.ts builder '#6750A4' --format css
|
|
8
|
-
// $ npx tsx src/cli.ts builder '#6750A4' --adaptive-shades --format figma
|
|
9
|
-
// ```
|
|
10
|
-
|
|
11
|
-
import * as fs from "node:fs";
|
|
12
|
-
import * as path from "node:path";
|
|
13
|
-
|
|
14
|
-
import { Command, Option } from "commander";
|
|
15
|
-
import {
|
|
16
|
-
builder,
|
|
17
|
-
DEFAULT_ADAPTIVE_SHADES,
|
|
18
|
-
DEFAULT_BLEND,
|
|
19
|
-
DEFAULT_CONTRAST,
|
|
20
|
-
DEFAULT_CONTRAST_ALL_COLORS,
|
|
21
|
-
DEFAULT_SCHEME,
|
|
22
|
-
schemeNames,
|
|
23
|
-
} from "react-mcu";
|
|
24
|
-
|
|
25
|
-
const program = new Command();
|
|
26
|
-
|
|
27
|
-
program.name("react-mcu").description("m3 color system for react");
|
|
28
|
-
|
|
29
|
-
program
|
|
30
|
-
.command("builder")
|
|
31
|
-
.description("Generate a color theme from a source color")
|
|
32
|
-
.argument("<source>", "Source color in hex format (e.g. #6750A4)")
|
|
33
|
-
.addOption(
|
|
34
|
-
new Option("--scheme <name>", "Color scheme variant")
|
|
35
|
-
.choices(schemeNames)
|
|
36
|
-
.default(DEFAULT_SCHEME),
|
|
37
|
-
)
|
|
38
|
-
.option(
|
|
39
|
-
"--contrast <number>",
|
|
40
|
-
"Contrast level from -1.0 to 1.0",
|
|
41
|
-
parseFloat,
|
|
42
|
-
DEFAULT_CONTRAST,
|
|
43
|
-
)
|
|
44
|
-
.option("--primary <hex>", "Primary color override")
|
|
45
|
-
.option("--secondary <hex>", "Secondary color override")
|
|
46
|
-
.option("--tertiary <hex>", "Tertiary color override")
|
|
47
|
-
.option("--error <hex>", "Error color override")
|
|
48
|
-
.option("--neutral <hex>", "Neutral color override")
|
|
49
|
-
.option("--neutral-variant <hex>", "Neutral variant color override")
|
|
50
|
-
.option(
|
|
51
|
-
"--custom-colors <json>",
|
|
52
|
-
'Custom colors as JSON array (e.g. \'[{"name":"brand","hex":"#FF5733","blend":true}]\')',
|
|
53
|
-
)
|
|
54
|
-
.option("--format <type>", "Output format: json, css, or figma", "figma")
|
|
55
|
-
.option("--output <dir>", "Output directory (required for figma format)")
|
|
56
|
-
.option(
|
|
57
|
-
"--adaptive-shades",
|
|
58
|
-
"Adapt tonal palette shades for dark mode",
|
|
59
|
-
DEFAULT_ADAPTIVE_SHADES,
|
|
60
|
-
)
|
|
61
|
-
.option(
|
|
62
|
-
"--contrast-all-colors",
|
|
63
|
-
"Apply contrast adjustment to tonal palette shades",
|
|
64
|
-
DEFAULT_CONTRAST_ALL_COLORS,
|
|
65
|
-
)
|
|
66
|
-
.action((source: string, opts) => {
|
|
67
|
-
let customColors: { name: string; hex: string; blend: boolean }[] = [];
|
|
68
|
-
if (opts.customColors) {
|
|
69
|
-
try {
|
|
70
|
-
const parsed = JSON.parse(opts.customColors) as {
|
|
71
|
-
name: string;
|
|
72
|
-
hex: string;
|
|
73
|
-
blend?: boolean;
|
|
74
|
-
}[];
|
|
75
|
-
customColors = parsed.map((c) => ({
|
|
76
|
-
name: c.name,
|
|
77
|
-
hex: c.hex,
|
|
78
|
-
blend: c.blend ?? DEFAULT_BLEND,
|
|
79
|
-
}));
|
|
80
|
-
} catch {
|
|
81
|
-
console.error("Error: --custom-colors must be valid JSON");
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const result = builder(source, {
|
|
87
|
-
scheme: opts.scheme,
|
|
88
|
-
contrast: opts.contrast,
|
|
89
|
-
primary: opts.primary,
|
|
90
|
-
secondary: opts.secondary,
|
|
91
|
-
tertiary: opts.tertiary,
|
|
92
|
-
error: opts.error,
|
|
93
|
-
neutral: opts.neutral,
|
|
94
|
-
neutralVariant: opts.neutralVariant,
|
|
95
|
-
customColors,
|
|
96
|
-
adaptiveShades: opts.adaptiveShades,
|
|
97
|
-
contrastAllColors: opts.contrastAllColors,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
if (opts.format === "css") {
|
|
101
|
-
process.stdout.write(result.toCss());
|
|
102
|
-
} else if (opts.format === "figma") {
|
|
103
|
-
const outputDir = opts.output ?? "mcu-theme";
|
|
104
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
105
|
-
const files = result.toFigmaTokens();
|
|
106
|
-
for (const [filename, content] of Object.entries(files)) {
|
|
107
|
-
const filePath = path.join(outputDir, filename);
|
|
108
|
-
fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + "\n");
|
|
109
|
-
console.error(`wrote ${filePath}`);
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
process.stdout.write(JSON.stringify(result.toJson(), null, 2) + "\n");
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
program.parse();
|