react-mcu 1.3.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -38
- package/dist/cli.js +230 -131
- package/dist/index.d.ts +94 -67
- package/dist/index.js +229 -129
- package/dist/tailwind.css +157 -143
- package/package.json +3 -3
- package/src/tailwind.css +157 -143
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[Material Design colors](https://m3.material.io/styles/color/system/overview)
|
|
6
6
|
for React.
|
|
7
7
|
|
|
8
|
-
It injects `--
|
|
8
|
+
It injects `--md-sys-color-*` and `--md-ref-palette-*` CSS variables into the page (prefix is [configurable](#programmatic-api)).
|
|
9
9
|
|
|
10
10
|
https://github.com/user-attachments/assets/5b67c961-d7a4-4b64-9356-4ada26bc9be4
|
|
11
11
|
|
|
@@ -54,12 +54,12 @@ import { Mcu } from "react-mcu";
|
|
|
54
54
|
]}
|
|
55
55
|
>
|
|
56
56
|
<p style={{
|
|
57
|
-
backgroundColor: "var(--
|
|
58
|
-
color: "var(--
|
|
57
|
+
backgroundColor: "var(--md-sys-color-surface)",
|
|
58
|
+
color: "var(--md-sys-color-on-surface)",
|
|
59
59
|
}}>
|
|
60
60
|
Hello, MCU <span style={{
|
|
61
|
-
backgroundColor: "var(--
|
|
62
|
-
color: "var(--
|
|
61
|
+
backgroundColor: "var(--md-sys-color-my-custom-color-1)",
|
|
62
|
+
color: "var(--md-sys-color-on-my-custom-color-1)",
|
|
63
63
|
}}>colors<span>!
|
|
64
64
|
</p>
|
|
65
65
|
</Mcu>
|
|
@@ -72,8 +72,8 @@ import { Mcu } from "react-mcu";
|
|
|
72
72
|
|
|
73
73
|
> [!NOTE]
|
|
74
74
|
>
|
|
75
|
-
> CSS varnames are always kebab-cased, `myCustomColor1` →
|
|
76
|
-
> `--
|
|
75
|
+
> CSS varnames are always kebab-cased, e.g. `myCustomColor1` →
|
|
76
|
+
> `--md-sys-color-my-custom-color-1` / `--md-ref-palette-my-custom-color-1-<tone>`
|
|
77
77
|
|
|
78
78
|
## `useMcu`
|
|
79
79
|
|
|
@@ -128,37 +128,37 @@ Simply override/remap
|
|
|
128
128
|
|
|
129
129
|
:root,
|
|
130
130
|
.dark {
|
|
131
|
-
--background: var(--
|
|
132
|
-
--foreground: var(--
|
|
133
|
-
--card: var(--
|
|
134
|
-
--card-foreground: var(--
|
|
135
|
-
--popover: var(--
|
|
136
|
-
--popover-foreground: var(--
|
|
137
|
-
--primary: var(--
|
|
138
|
-
--primary-foreground: var(--
|
|
139
|
-
--secondary: var(--
|
|
140
|
-
--secondary-foreground: var(--
|
|
141
|
-
--muted: var(--
|
|
142
|
-
--muted-foreground: var(--
|
|
143
|
-
--accent: var(--
|
|
144
|
-
--accent-foreground: var(--
|
|
145
|
-
--destructive: var(--
|
|
146
|
-
--border: var(--
|
|
147
|
-
--input: var(--
|
|
148
|
-
--ring: var(--
|
|
149
|
-
--chart-1: var(--
|
|
150
|
-
--chart-2: var(--
|
|
151
|
-
--chart-3: var(--
|
|
152
|
-
--chart-4: var(--
|
|
153
|
-
--chart-5: var(--
|
|
154
|
-
--sidebar: var(--
|
|
155
|
-
--sidebar-foreground: var(--
|
|
156
|
-
--sidebar-primary: var(--
|
|
157
|
-
--sidebar-primary-foreground: var(--
|
|
158
|
-
--sidebar-accent: var(--
|
|
159
|
-
--sidebar-accent-foreground: var(--
|
|
160
|
-
--sidebar-border: var(--
|
|
161
|
-
--sidebar-ring: var(--
|
|
131
|
+
--background: var(--md-sys-color-surface);
|
|
132
|
+
--foreground: var(--md-sys-color-on-surface);
|
|
133
|
+
--card: var(--md-sys-color-surface-container-low);
|
|
134
|
+
--card-foreground: var(--md-sys-color-on-surface);
|
|
135
|
+
--popover: var(--md-sys-color-surface-container-high);
|
|
136
|
+
--popover-foreground: var(--md-sys-color-on-surface);
|
|
137
|
+
--primary: var(--md-sys-color-primary);
|
|
138
|
+
--primary-foreground: var(--md-sys-color-on-primary);
|
|
139
|
+
--secondary: var(--md-sys-color-secondary-container);
|
|
140
|
+
--secondary-foreground: var(--md-sys-color-on-secondary-container);
|
|
141
|
+
--muted: var(--md-sys-color-surface-container-highest);
|
|
142
|
+
--muted-foreground: var(--md-sys-color-on-surface-variant);
|
|
143
|
+
--accent: var(--md-sys-color-secondary-container);
|
|
144
|
+
--accent-foreground: var(--md-sys-color-on-secondary-container);
|
|
145
|
+
--destructive: var(--md-sys-color-error);
|
|
146
|
+
--border: var(--md-sys-color-outline-variant);
|
|
147
|
+
--input: var(--md-sys-color-outline);
|
|
148
|
+
--ring: var(--md-sys-color-primary);
|
|
149
|
+
--chart-1: var(--md-sys-color-primary-fixed);
|
|
150
|
+
--chart-2: var(--md-sys-color-secondary-fixed);
|
|
151
|
+
--chart-3: var(--md-sys-color-tertiary-fixed);
|
|
152
|
+
--chart-4: var(--md-sys-color-primary-fixed-dim);
|
|
153
|
+
--chart-5: var(--md-sys-color-secondary-fixed-dim);
|
|
154
|
+
--sidebar: var(--md-sys-color-surface-container-low);
|
|
155
|
+
--sidebar-foreground: var(--md-sys-color-on-surface);
|
|
156
|
+
--sidebar-primary: var(--md-sys-color-primary);
|
|
157
|
+
--sidebar-primary-foreground: var(--md-sys-color-on-primary);
|
|
158
|
+
--sidebar-accent: var(--md-sys-color-secondary-container);
|
|
159
|
+
--sidebar-accent-foreground: var(--md-sys-color-on-secondary-container);
|
|
160
|
+
--sidebar-border: var(--md-sys-color-outline-variant);
|
|
161
|
+
--sidebar-ring: var(--md-sys-color-primary);
|
|
162
162
|
}
|
|
163
163
|
```
|
|
164
164
|
|
package/dist/cli.js
CHANGED
|
@@ -27,13 +27,6 @@ import {
|
|
|
27
27
|
TonalPalette
|
|
28
28
|
} from "@material/material-color-utilities";
|
|
29
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
30
|
var schemeNames = [
|
|
38
31
|
"tonalSpot",
|
|
39
32
|
"monochrome",
|
|
@@ -55,11 +48,39 @@ var schemesMap = {
|
|
|
55
48
|
var DEFAULT_SCHEME = "tonalSpot";
|
|
56
49
|
var DEFAULT_CONTRAST = 0;
|
|
57
50
|
var DEFAULT_CUSTOM_COLORS = [];
|
|
58
|
-
var DEFAULT_CONTRAST_ALL_COLORS = false;
|
|
59
|
-
var DEFAULT_ADAPTIVE_SHADES = false;
|
|
60
51
|
var DEFAULT_BLEND = true;
|
|
61
|
-
var
|
|
52
|
+
var DEFAULT_PREFIX = "md";
|
|
62
53
|
var STANDARD_TONES = [
|
|
54
|
+
0,
|
|
55
|
+
4,
|
|
56
|
+
5,
|
|
57
|
+
6,
|
|
58
|
+
10,
|
|
59
|
+
12,
|
|
60
|
+
15,
|
|
61
|
+
17,
|
|
62
|
+
20,
|
|
63
|
+
22,
|
|
64
|
+
24,
|
|
65
|
+
25,
|
|
66
|
+
30,
|
|
67
|
+
35,
|
|
68
|
+
40,
|
|
69
|
+
50,
|
|
70
|
+
60,
|
|
71
|
+
70,
|
|
72
|
+
80,
|
|
73
|
+
87,
|
|
74
|
+
90,
|
|
75
|
+
92,
|
|
76
|
+
94,
|
|
77
|
+
95,
|
|
78
|
+
96,
|
|
79
|
+
98,
|
|
80
|
+
99,
|
|
81
|
+
100
|
|
82
|
+
];
|
|
83
|
+
var MTB_TONES = [
|
|
63
84
|
0,
|
|
64
85
|
5,
|
|
65
86
|
10,
|
|
@@ -99,57 +120,60 @@ var schemeToVariant = {
|
|
|
99
120
|
fidelity: Variant.FIDELITY,
|
|
100
121
|
content: Variant.CONTENT
|
|
101
122
|
};
|
|
102
|
-
var
|
|
103
|
-
"background",
|
|
104
|
-
"error",
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"surface",
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
123
|
+
var tokenDescriptions = {
|
|
124
|
+
background: "Default background color for screens and large surfaces.",
|
|
125
|
+
error: "Color for error states, used on elements like error text and icons.",
|
|
126
|
+
errorContainer: "Fill color for error container elements like error banners.",
|
|
127
|
+
inverseOnSurface: "Color for text and icons on inverse surface backgrounds.",
|
|
128
|
+
inversePrimary: "Primary color used on inverse surface, e.g. buttons on snackbars.",
|
|
129
|
+
inverseSurface: "Background for elements that require reverse contrast, such as snackbars.",
|
|
130
|
+
onBackground: "Color for text and icons displayed on the background.",
|
|
131
|
+
onError: "Color for text and icons on error-colored elements.",
|
|
132
|
+
onErrorContainer: "Color for text and icons on error container elements.",
|
|
133
|
+
onPrimary: "Color for text and icons on primary-colored elements like filled buttons.",
|
|
134
|
+
onPrimaryContainer: "Color for text and icons on primary container elements like tonal buttons.",
|
|
135
|
+
onPrimaryFixed: "Color for text and icons on primary fixed elements, constant across themes.",
|
|
136
|
+
onPrimaryFixedVariant: "Lower-emphasis color for text and icons on primary fixed elements.",
|
|
137
|
+
onSecondary: "Color for text and icons on secondary-colored elements.",
|
|
138
|
+
onSecondaryContainer: "Color for text and icons on secondary container elements.",
|
|
139
|
+
onSecondaryFixed: "Color for text and icons on secondary fixed elements, constant across themes.",
|
|
140
|
+
onSecondaryFixedVariant: "Lower-emphasis color for text and icons on secondary fixed elements.",
|
|
141
|
+
onSurface: "High-emphasis color for text and icons on surface backgrounds.",
|
|
142
|
+
onSurfaceVariant: "Medium-emphasis color for text and icons on surface variant backgrounds.",
|
|
143
|
+
onTertiary: "Color for text and icons on tertiary-colored elements.",
|
|
144
|
+
onTertiaryContainer: "Color for text and icons on tertiary container elements.",
|
|
145
|
+
onTertiaryFixed: "Color for text and icons on tertiary fixed elements, constant across themes.",
|
|
146
|
+
onTertiaryFixedVariant: "Lower-emphasis color for text and icons on tertiary fixed elements.",
|
|
147
|
+
outline: "Subtle color for borders and dividers to create visual separation.",
|
|
148
|
+
outlineVariant: "Lower-emphasis border color used for decorative dividers.",
|
|
149
|
+
primary: "Main brand color, used for key components like filled buttons and active states.",
|
|
150
|
+
primaryContainer: "Fill color for large primary elements like cards and tonal buttons.",
|
|
151
|
+
primaryFixed: "Fixed primary color that stays the same in light and dark themes.",
|
|
152
|
+
primaryFixedDim: "Dimmed variant of the fixed primary color for lower emphasis.",
|
|
153
|
+
scrim: "Color overlay for modals and dialogs to obscure background content.",
|
|
154
|
+
secondary: "Accent color for less prominent elements like filter chips and selections.",
|
|
155
|
+
secondaryContainer: "Fill color for secondary container elements like tonal buttons and input fields.",
|
|
156
|
+
secondaryFixed: "Fixed secondary color that stays the same in light and dark themes.",
|
|
157
|
+
secondaryFixedDim: "Dimmed variant of the fixed secondary color for lower emphasis.",
|
|
158
|
+
shadow: "Color for elevation shadows applied to surfaces and components.",
|
|
159
|
+
surface: "Default surface color for cards, sheets, and dialogs.",
|
|
160
|
+
surfaceBright: "Brightest surface variant, used for elevated surfaces in dark themes.",
|
|
161
|
+
surfaceContainer: "Middle-emphasis container color for grouping related content.",
|
|
162
|
+
surfaceContainerHigh: "Higher-emphasis container color for elements like cards.",
|
|
163
|
+
surfaceContainerHighest: "Highest-emphasis container color for text fields and other input areas.",
|
|
164
|
+
surfaceContainerLow: "Lower-emphasis container color for subtle surface groupings.",
|
|
165
|
+
surfaceContainerLowest: "Lowest-emphasis container, typically the lightest surface in light theme.",
|
|
166
|
+
surfaceDim: "Dimmest surface variant, used for recessed areas or dark theme backgrounds.",
|
|
167
|
+
surfaceTint: "Tint color applied to surfaces for subtle primary color elevation overlay.",
|
|
168
|
+
surfaceVariant: "Alternative surface color for differentiated areas like sidebar backgrounds.",
|
|
169
|
+
tertiary: "Third accent color for complementary elements that balance primary and secondary.",
|
|
170
|
+
tertiaryContainer: "Fill color for tertiary container elements like complementary cards.",
|
|
171
|
+
tertiaryFixed: "Fixed tertiary color that stays the same in light and dark themes.",
|
|
172
|
+
tertiaryFixedDim: "Dimmed variant of the fixed tertiary color for lower emphasis."
|
|
173
|
+
};
|
|
174
|
+
var tokenNames = Object.keys(
|
|
175
|
+
tokenDescriptions
|
|
176
|
+
);
|
|
153
177
|
function toRecord(arr, getEntry) {
|
|
154
178
|
return Object.fromEntries(arr.map(getEntry));
|
|
155
179
|
}
|
|
@@ -162,7 +186,7 @@ function getPalette(palettes, colorName) {
|
|
|
162
186
|
}
|
|
163
187
|
return palette;
|
|
164
188
|
}
|
|
165
|
-
function mergeBaseAndCustomColors(scheme, customColors, colorPalettes
|
|
189
|
+
function mergeBaseAndCustomColors(scheme, customColors, colorPalettes) {
|
|
166
190
|
const baseVars = toRecord(tokenNames, (tokenName) => {
|
|
167
191
|
const dynamicColor = MaterialDynamicColors[tokenName];
|
|
168
192
|
const argb = dynamicColor.getArgb(scheme);
|
|
@@ -172,9 +196,8 @@ function mergeBaseAndCustomColors(scheme, customColors, colorPalettes, contrastA
|
|
|
172
196
|
customColors.forEach((color) => {
|
|
173
197
|
const colorname = color.name;
|
|
174
198
|
const getPaletteForColor = (s) => getPalette(colorPalettes, colorname);
|
|
175
|
-
const getTone = (baseTone) => (
|
|
176
|
-
|
|
177
|
-
return adjustToneForContrast(baseTone, s.contrastLevel);
|
|
199
|
+
const getTone = (baseTone) => (_s) => {
|
|
200
|
+
return baseTone;
|
|
178
201
|
};
|
|
179
202
|
const colorDynamicColor = new DynamicColor(
|
|
180
203
|
colorname,
|
|
@@ -241,8 +264,7 @@ function builder(hexSource, {
|
|
|
241
264
|
neutralVariant,
|
|
242
265
|
error,
|
|
243
266
|
customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS,
|
|
244
|
-
|
|
245
|
-
adaptiveShades = DEFAULT_ADAPTIVE_SHADES
|
|
267
|
+
prefix = DEFAULT_PREFIX
|
|
246
268
|
} = {}) {
|
|
247
269
|
const sourceArgb = argbFromHex(hexSource);
|
|
248
270
|
const sourceHct = Hct.fromInt(sourceArgb);
|
|
@@ -342,15 +364,49 @@ function builder(hexSource, {
|
|
|
342
364
|
const mergedColorsLight = mergeBaseAndCustomColors(
|
|
343
365
|
lightScheme,
|
|
344
366
|
customColors,
|
|
345
|
-
colorPalettes
|
|
346
|
-
contrastAllColors
|
|
367
|
+
colorPalettes
|
|
347
368
|
);
|
|
348
369
|
const mergedColorsDark = mergeBaseAndCustomColors(
|
|
349
370
|
darkScheme,
|
|
350
371
|
customColors,
|
|
351
|
-
colorPalettes
|
|
352
|
-
contrastAllColors
|
|
372
|
+
colorPalettes
|
|
353
373
|
);
|
|
374
|
+
const schemePalettes = [
|
|
375
|
+
["primary", lightScheme.primaryPalette],
|
|
376
|
+
["secondary", lightScheme.secondaryPalette],
|
|
377
|
+
["tertiary", lightScheme.tertiaryPalette],
|
|
378
|
+
["error", lightScheme.errorPalette],
|
|
379
|
+
["neutral", lightScheme.neutralPalette],
|
|
380
|
+
["neutral-variant", lightScheme.neutralVariantPalette]
|
|
381
|
+
];
|
|
382
|
+
function buildTokenToPaletteMap(schemePalettes2, scheme2) {
|
|
383
|
+
const result = {};
|
|
384
|
+
for (const propName of Object.getOwnPropertyNames(MaterialDynamicColors)) {
|
|
385
|
+
const dc = MaterialDynamicColors[propName];
|
|
386
|
+
if (!(dc instanceof DynamicColor)) continue;
|
|
387
|
+
const palette = dc.palette(scheme2);
|
|
388
|
+
for (const [palName, pal] of schemePalettes2) {
|
|
389
|
+
if (palette === pal) {
|
|
390
|
+
result[propName] = palName;
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
const tokenToPalette = buildTokenToPaletteMap(schemePalettes, lightScheme);
|
|
398
|
+
const allPaletteNamesKebab = new Set(Object.keys(allPalettes).map(kebabCase));
|
|
399
|
+
function deriveCustomPaletteName(tokenName) {
|
|
400
|
+
let baseName = tokenName;
|
|
401
|
+
if (/^on[A-Z]/.test(baseName) && baseName.length > 2) {
|
|
402
|
+
baseName = baseName.charAt(2).toLowerCase() + baseName.slice(3);
|
|
403
|
+
}
|
|
404
|
+
if (baseName.endsWith("Container")) {
|
|
405
|
+
baseName = baseName.slice(0, -"Container".length);
|
|
406
|
+
}
|
|
407
|
+
const kebab = kebabCase(baseName);
|
|
408
|
+
return allPaletteNamesKebab.has(kebab) ? kebab : void 0;
|
|
409
|
+
}
|
|
354
410
|
return {
|
|
355
411
|
//
|
|
356
412
|
// ██████ ███████ ███████
|
|
@@ -360,45 +416,55 @@ function builder(hexSource, {
|
|
|
360
416
|
// ██████ ███████ ███████
|
|
361
417
|
//
|
|
362
418
|
toCss() {
|
|
363
|
-
function
|
|
364
|
-
const
|
|
419
|
+
function buildRefPaletteLookup() {
|
|
420
|
+
const lookup = {};
|
|
421
|
+
for (const [name, palette] of Object.entries(allPalettes)) {
|
|
422
|
+
const paletteName = kebabCase(name);
|
|
423
|
+
for (const tone of STANDARD_TONES) {
|
|
424
|
+
const hex = hexFromArgb(palette.tone(tone));
|
|
425
|
+
if (!lookup[hex]) lookup[hex] = [];
|
|
426
|
+
lookup[hex].push({ paletteName, tone });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return lookup;
|
|
430
|
+
}
|
|
431
|
+
function sysColorVar(colorName, colorValue, lookup) {
|
|
432
|
+
const name = `--${prefix}-sys-color-${kebabCase(colorName)}`;
|
|
433
|
+
const hex = hexFromArgb(colorValue);
|
|
434
|
+
const matches = lookup[hex];
|
|
435
|
+
if (matches && matches.length > 0) {
|
|
436
|
+
const preferred = tokenToPalette[colorName] ?? deriveCustomPaletteName(colorName);
|
|
437
|
+
const match = (preferred ? matches.find((m) => m.paletteName === preferred) : void 0) ?? matches[0];
|
|
438
|
+
return `${name}:var(--${prefix}-ref-palette-${match.paletteName}-${match.tone});`;
|
|
439
|
+
}
|
|
440
|
+
return `${name}:${hex};`;
|
|
441
|
+
}
|
|
442
|
+
function toCssVars(mergedColors, lookup) {
|
|
443
|
+
return Object.entries(mergedColors).map(([name, value]) => sysColorVar(name, value, lookup)).join(" ");
|
|
444
|
+
}
|
|
445
|
+
function refPaletteVar(paletteName, tone, colorValue) {
|
|
446
|
+
const name = `--${prefix}-ref-palette-${paletteName}-${tone}`;
|
|
365
447
|
const value = hexFromArgb(colorValue);
|
|
366
448
|
return `${name}:${value};`;
|
|
367
449
|
}
|
|
368
|
-
function
|
|
369
|
-
return Object.entries(mergedColors).map(([name, value]) => cssVar(name, value)).join(" ");
|
|
370
|
-
}
|
|
371
|
-
function generateTonalPaletteVars(paletteName, palette, scheme2, applyContrast, adaptiveShades2) {
|
|
450
|
+
function generateTonalPaletteVars(paletteName, palette) {
|
|
372
451
|
return STANDARD_TONES.map((tone) => {
|
|
373
|
-
|
|
374
|
-
|
|
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);
|
|
452
|
+
const color = palette.tone(tone);
|
|
453
|
+
return refPaletteVar(paletteName, tone, color);
|
|
382
454
|
}).join(" ");
|
|
383
455
|
}
|
|
384
|
-
function generateTonalVars(
|
|
456
|
+
function generateTonalVars() {
|
|
385
457
|
return Object.entries(allPalettes).map(
|
|
386
|
-
([name, palette]) => generateTonalPaletteVars(
|
|
387
|
-
kebabCase(name),
|
|
388
|
-
palette,
|
|
389
|
-
s,
|
|
390
|
-
contrastAllColors,
|
|
391
|
-
adaptiveShades
|
|
392
|
-
)
|
|
458
|
+
([name, palette]) => generateTonalPaletteVars(kebabCase(name), palette)
|
|
393
459
|
).join(" ");
|
|
394
460
|
}
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
const
|
|
461
|
+
const refPaletteLookup = buildRefPaletteLookup();
|
|
462
|
+
const lightVars = toCssVars(mergedColorsLight, refPaletteLookup);
|
|
463
|
+
const darkVars = toCssVars(mergedColorsDark, refPaletteLookup);
|
|
464
|
+
const tonalVars = generateTonalVars();
|
|
399
465
|
return `
|
|
400
|
-
:root { ${lightVars} ${
|
|
401
|
-
.dark { ${darkVars} ${
|
|
466
|
+
:root { ${lightVars} ${tonalVars} }
|
|
467
|
+
.dark { ${darkVars} ${tonalVars} }
|
|
402
468
|
`;
|
|
403
469
|
},
|
|
404
470
|
//
|
|
@@ -541,7 +607,7 @@ function builder(hexSource, {
|
|
|
541
607
|
for (const name of RAW_PALETTE_NAMES) {
|
|
542
608
|
const palette = rawPalettes[name];
|
|
543
609
|
const tones = {};
|
|
544
|
-
for (const tone of
|
|
610
|
+
for (const tone of MTB_TONES) {
|
|
545
611
|
tones[tone.toString()] = hexFromArgb(
|
|
546
612
|
palette.tone(tone)
|
|
547
613
|
).toUpperCase();
|
|
@@ -613,44 +679,82 @@ function builder(hexSource, {
|
|
|
613
679
|
}
|
|
614
680
|
};
|
|
615
681
|
}
|
|
616
|
-
function
|
|
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) {
|
|
682
|
+
function buildRefPaletteTokens() {
|
|
624
683
|
const palettes = {};
|
|
625
684
|
for (const [name, palette] of Object.entries(allPalettes)) {
|
|
626
685
|
const tones = {};
|
|
627
686
|
for (const tone of STANDARD_TONES) {
|
|
628
|
-
|
|
629
|
-
if (adaptiveShades && isDark) {
|
|
630
|
-
toneToUse = 100 - tone;
|
|
631
|
-
}
|
|
632
|
-
if (contrastAllColors) {
|
|
633
|
-
toneToUse = adjustToneForContrast(toneToUse, contrast);
|
|
634
|
-
}
|
|
635
|
-
const argb = palette.tone(toneToUse);
|
|
687
|
+
const argb = palette.tone(tone);
|
|
636
688
|
tones[tone.toString()] = figmaToken(argb);
|
|
637
689
|
}
|
|
638
690
|
palettes[startCase(name)] = tones;
|
|
639
691
|
}
|
|
640
692
|
return palettes;
|
|
641
693
|
}
|
|
642
|
-
function
|
|
694
|
+
function findToneInPalette(hex, paletteName, tones) {
|
|
695
|
+
for (const [tone, token] of Object.entries(tones)) {
|
|
696
|
+
if (token.$value.hex === hex) {
|
|
697
|
+
return `{ref.palette.${paletteName}.${tone}}`;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
function findAlias(hex, tokenName, refPalettes2) {
|
|
703
|
+
const preferredPaletteKebab = tokenToPalette[tokenName] ?? deriveCustomPaletteName(tokenName);
|
|
704
|
+
const preferredPalette = preferredPaletteKebab ? startCase(preferredPaletteKebab) : void 0;
|
|
705
|
+
if (preferredPalette && refPalettes2[preferredPalette]) {
|
|
706
|
+
const match = findToneInPalette(
|
|
707
|
+
hex,
|
|
708
|
+
preferredPalette,
|
|
709
|
+
refPalettes2[preferredPalette]
|
|
710
|
+
);
|
|
711
|
+
if (match) return match;
|
|
712
|
+
}
|
|
713
|
+
for (const [palName, tones] of Object.entries(refPalettes2)) {
|
|
714
|
+
const match = findToneInPalette(hex, palName, tones);
|
|
715
|
+
if (match) return match;
|
|
716
|
+
}
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
function resolveModeValue(argb, tokenName, refPalettes2) {
|
|
720
|
+
const hex = hexFromArgb(argb).toUpperCase();
|
|
721
|
+
return findAlias(hex, tokenName, refPalettes2) ?? argbToFigmaColorValue(argb);
|
|
722
|
+
}
|
|
723
|
+
function buildSysColorTokens(mergedColors, refPalettes2) {
|
|
724
|
+
const tokens = {};
|
|
725
|
+
for (const [name, argb] of Object.entries(mergedColors)) {
|
|
726
|
+
const description = tokenDescriptions[name];
|
|
727
|
+
const cssVar = `--${prefix}-sys-color-${kebabCase(name)}`;
|
|
728
|
+
const value = resolveModeValue(argb, name, refPalettes2);
|
|
729
|
+
tokens[startCase(name)] = {
|
|
730
|
+
$type: "color",
|
|
731
|
+
$value: value,
|
|
732
|
+
...description ? { $description: description } : {},
|
|
733
|
+
$extensions: {
|
|
734
|
+
"com.figma.scopes": ["ALL_SCOPES"],
|
|
735
|
+
"css.variable": cssVar
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
return tokens;
|
|
740
|
+
}
|
|
741
|
+
const refPalettes = buildRefPaletteTokens();
|
|
742
|
+
function buildModeFile(modeName, mergedColors) {
|
|
643
743
|
return {
|
|
644
|
-
|
|
645
|
-
|
|
744
|
+
ref: {
|
|
745
|
+
palette: refPalettes
|
|
746
|
+
},
|
|
747
|
+
sys: {
|
|
748
|
+
color: buildSysColorTokens(mergedColors, refPalettes)
|
|
749
|
+
},
|
|
646
750
|
$extensions: {
|
|
647
751
|
"com.figma.modeName": modeName
|
|
648
752
|
}
|
|
649
753
|
};
|
|
650
754
|
}
|
|
651
755
|
return {
|
|
652
|
-
"Light.tokens.json": buildModeFile("Light", mergedColorsLight
|
|
653
|
-
"Dark.tokens.json": buildModeFile("Dark", mergedColorsDark
|
|
756
|
+
"Light.tokens.json": buildModeFile("Light", mergedColorsLight),
|
|
757
|
+
"Dark.tokens.json": buildModeFile("Dark", mergedColorsDark)
|
|
654
758
|
};
|
|
655
759
|
},
|
|
656
760
|
//
|
|
@@ -676,13 +780,9 @@ program.command("builder").description("Generate a color theme from a source col
|
|
|
676
780
|
"--custom-colors <json>",
|
|
677
781
|
`Custom colors as JSON array (e.g. '[{"name":"brand","hex":"#FF5733","blend":true}]')`
|
|
678
782
|
).option("--format <type>", "Output format: json, css, or figma", "figma").option("--output <dir>", "Output directory (required for figma format)").option(
|
|
679
|
-
"--
|
|
680
|
-
"
|
|
681
|
-
|
|
682
|
-
).option(
|
|
683
|
-
"--contrast-all-colors",
|
|
684
|
-
"Apply contrast adjustment to tonal palette shades",
|
|
685
|
-
DEFAULT_CONTRAST_ALL_COLORS
|
|
783
|
+
"--prefix <string>",
|
|
784
|
+
"CSS variable prefix (e.g. md \u2192 --md-sys-color-*, --md-ref-palette-*)",
|
|
785
|
+
DEFAULT_PREFIX
|
|
686
786
|
).action((source, opts) => {
|
|
687
787
|
let customColors = [];
|
|
688
788
|
if (opts.customColors) {
|
|
@@ -708,8 +808,7 @@ program.command("builder").description("Generate a color theme from a source col
|
|
|
708
808
|
neutral: opts.neutral,
|
|
709
809
|
neutralVariant: opts.neutralVariant,
|
|
710
810
|
customColors,
|
|
711
|
-
|
|
712
|
-
contrastAllColors: opts.contrastAllColors
|
|
811
|
+
prefix: opts.prefix
|
|
713
812
|
});
|
|
714
813
|
if (opts.format === "css") {
|
|
715
814
|
process.stdout.write(result.toCss());
|