react-mcu 1.1.1 → 1.2.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 +21 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +100 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,27 @@ m3 references:
|
|
|
15
15
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
16
|
| [<img width="2836" height="2266" alt="CleanShot 2026-01-14 at 08 58 40@2x" src="https://github.com/user-attachments/assets/e4b47c00-716f-4b08-b393-de306d5ce302" />](https://material-foundation.github.io/material-theme-builder/) | [<img width="2836" height="2266" alt="CleanShot 2026-01-14 at 09 01 23@2x" src="https://github.com/user-attachments/assets/826e502d-e173-43c4-807a-53d0ba075a88" />](https://m3.material.io/styles/color/roles) |
|
|
17
17
|
|
|
18
|
+
Support for:
|
|
19
|
+
|
|
20
|
+
Base (like in the Builder):
|
|
21
|
+
|
|
22
|
+
- [x] light/dark mode
|
|
23
|
+
- [x] source color
|
|
24
|
+
- [x] scheme
|
|
25
|
+
- [x] contrast
|
|
26
|
+
- [x] core-colors overrides: primary, secondary, tertiary, error, neutral,
|
|
27
|
+
neutralVariant
|
|
28
|
+
- [x] custom-colors (aka. "Extended colors")
|
|
29
|
+
- [x] Harmonization (aka. `blend`) -- with effective color: `source` or
|
|
30
|
+
`primary` if defined
|
|
31
|
+
- [x] Shades (aka. "tonals")
|
|
32
|
+
- [ ] colorMatch
|
|
33
|
+
|
|
34
|
+
Extra:
|
|
35
|
+
|
|
36
|
+
- [x] `contrastAllColors`: contrast also applies to custom-colors and shades
|
|
37
|
+
(not only the core-colors)
|
|
38
|
+
|
|
18
39
|
# Usage
|
|
19
40
|
|
|
20
41
|
```tsx
|
package/dist/index.d.ts
CHANGED
|
@@ -45,6 +45,13 @@ type McuConfig = {
|
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
47
|
customColors?: HexCustomColor[];
|
|
48
|
+
/**
|
|
49
|
+
* When true, applies the contrast level to all colors including custom colors and tonal palette shades.
|
|
50
|
+
* When false (default), only core colors are affected by the contrast level.
|
|
51
|
+
*
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
contrastAllColors?: boolean;
|
|
48
55
|
};
|
|
49
56
|
declare const schemesMap: {
|
|
50
57
|
readonly tonalSpot: typeof SchemeTonalSpot;
|
|
@@ -57,7 +64,7 @@ declare const schemesMap: {
|
|
|
57
64
|
};
|
|
58
65
|
declare const schemeNames: (keyof typeof schemesMap)[];
|
|
59
66
|
type SchemeName = (typeof schemeNames)[number];
|
|
60
|
-
declare function Mcu({ source, scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, colorMatch, customColors, children, }: McuConfig & {
|
|
67
|
+
declare function Mcu({ source, scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, colorMatch, customColors, contrastAllColors, children, }: McuConfig & {
|
|
61
68
|
children?: React.ReactNode;
|
|
62
69
|
}): react_jsx_runtime.JSX.Element;
|
|
63
70
|
declare const tokenNames: readonly ["background", "onBackground", "surface", "surfaceDim", "surfaceBright", "surfaceContainerLowest", "surfaceContainerLow", "surfaceContainer", "surfaceContainerHigh", "surfaceContainerHighest", "onSurface", "onSurfaceVariant", "outline", "outlineVariant", "inverseSurface", "inverseOnSurface", "primary", "onPrimary", "primaryContainer", "onPrimaryContainer", "primaryFixed", "primaryFixedDim", "onPrimaryFixed", "onPrimaryFixedVariant", "inversePrimary", "primaryFixed", "primaryFixedDim", "onPrimaryFixed", "onPrimaryFixedVariant", "secondary", "onSecondary", "secondaryContainer", "onSecondaryContainer", "secondaryFixed", "secondaryFixedDim", "onSecondaryFixed", "onSecondaryFixedVariant", "tertiary", "onTertiary", "tertiaryContainer", "onTertiaryContainer", "tertiaryFixed", "tertiaryFixedDim", "onTertiaryFixed", "onTertiaryFixedVariant", "error", "onError", "errorContainer", "onErrorContainer", "scrim", "shadow"];
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ var McuProvider = ({
|
|
|
52
52
|
scheme: initialScheme,
|
|
53
53
|
contrast: initialContrast,
|
|
54
54
|
customColors: initialCustomColors,
|
|
55
|
+
contrastAllColors: initialContrastAllColors,
|
|
55
56
|
styleId,
|
|
56
57
|
children
|
|
57
58
|
}) => {
|
|
@@ -59,7 +60,8 @@ var McuProvider = ({
|
|
|
59
60
|
source: initialSource,
|
|
60
61
|
scheme: initialScheme,
|
|
61
62
|
contrast: initialContrast,
|
|
62
|
-
customColors: initialCustomColors
|
|
63
|
+
customColors: initialCustomColors,
|
|
64
|
+
contrastAllColors: initialContrastAllColors
|
|
63
65
|
}));
|
|
64
66
|
const [mcuConfig, setMcuConfig] = useState(initials);
|
|
65
67
|
const { css, mergedColorsLight, mergedColorsDark } = useMemo(
|
|
@@ -96,6 +98,16 @@ var McuProvider = ({
|
|
|
96
98
|
|
|
97
99
|
// src/Mcu.tsx
|
|
98
100
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
101
|
+
function adjustToneForContrast(baseTone, contrastLevel, isDark, adjustmentFactor = DEFAULT_CONTRAST_ADJUSTMENT_FACTOR) {
|
|
102
|
+
if (contrastLevel === 0) return baseTone;
|
|
103
|
+
let adjustedTone;
|
|
104
|
+
if (isDark) {
|
|
105
|
+
adjustedTone = baseTone + contrastLevel * (100 - baseTone) * adjustmentFactor;
|
|
106
|
+
} else {
|
|
107
|
+
adjustedTone = baseTone - contrastLevel * baseTone * adjustmentFactor;
|
|
108
|
+
}
|
|
109
|
+
return Math.max(0, Math.min(100, adjustedTone));
|
|
110
|
+
}
|
|
99
111
|
var schemesMap = {
|
|
100
112
|
tonalSpot: SchemeTonalSpot,
|
|
101
113
|
monochrome: SchemeMonochrome,
|
|
@@ -112,6 +124,9 @@ var DEFAULT_SCHEME = "tonalSpot";
|
|
|
112
124
|
var DEFAULT_CONTRAST = 0;
|
|
113
125
|
var DEFAULT_COLOR_MATCH = false;
|
|
114
126
|
var DEFAULT_CUSTOM_COLORS = [];
|
|
127
|
+
var DEFAULT_CONTRAST_ALL_COLORS = false;
|
|
128
|
+
var DEFAULT_BLEND = true;
|
|
129
|
+
var DEFAULT_CONTRAST_ADJUSTMENT_FACTOR = 0.2;
|
|
115
130
|
var STANDARD_TONES = [
|
|
116
131
|
0,
|
|
117
132
|
5,
|
|
@@ -165,6 +180,7 @@ function Mcu({
|
|
|
165
180
|
error,
|
|
166
181
|
colorMatch = DEFAULT_COLOR_MATCH,
|
|
167
182
|
customColors = DEFAULT_CUSTOM_COLORS,
|
|
183
|
+
contrastAllColors = DEFAULT_CONTRAST_ALL_COLORS,
|
|
168
184
|
children
|
|
169
185
|
}) {
|
|
170
186
|
const config = useMemo2(
|
|
@@ -179,7 +195,9 @@ function Mcu({
|
|
|
179
195
|
neutralVariant,
|
|
180
196
|
error,
|
|
181
197
|
colorMatch,
|
|
182
|
-
customColors
|
|
198
|
+
customColors,
|
|
199
|
+
// extras features
|
|
200
|
+
contrastAllColors
|
|
183
201
|
}),
|
|
184
202
|
[
|
|
185
203
|
contrast,
|
|
@@ -192,7 +210,8 @@ function Mcu({
|
|
|
192
210
|
neutral,
|
|
193
211
|
neutralVariant,
|
|
194
212
|
error,
|
|
195
|
-
colorMatch
|
|
213
|
+
colorMatch,
|
|
214
|
+
contrastAllColors
|
|
196
215
|
]
|
|
197
216
|
);
|
|
198
217
|
const { css } = useMemo2(() => generateCss(config), [config]);
|
|
@@ -279,7 +298,7 @@ function getPalette(palettes, colorName) {
|
|
|
279
298
|
}
|
|
280
299
|
return palette;
|
|
281
300
|
}
|
|
282
|
-
function mergeBaseAndCustomColors(scheme, customColors, colorPalettes) {
|
|
301
|
+
function mergeBaseAndCustomColors(scheme, customColors, colorPalettes, contrastAllColors) {
|
|
283
302
|
const baseVars = toRecord(tokenNames, (tokenName) => {
|
|
284
303
|
const dynamicColor = MaterialDynamicColors[tokenName];
|
|
285
304
|
const argb = dynamicColor.getArgb(scheme);
|
|
@@ -288,32 +307,39 @@ function mergeBaseAndCustomColors(scheme, customColors, colorPalettes) {
|
|
|
288
307
|
const customVars = {};
|
|
289
308
|
customColors.forEach((color) => {
|
|
290
309
|
const colorname = color.name;
|
|
310
|
+
const getPaletteForColor = (s) => getPalette(colorPalettes, colorname);
|
|
311
|
+
const getTone = (baseTone) => (s) => {
|
|
312
|
+
if (!contrastAllColors) return baseTone;
|
|
313
|
+
return adjustToneForContrast(baseTone, s.contrastLevel, s.isDark);
|
|
314
|
+
};
|
|
291
315
|
const colorDynamicColor = new DynamicColor(
|
|
292
316
|
colorname,
|
|
293
|
-
|
|
294
|
-
(s) => s.isDark ? 80 : 40,
|
|
295
|
-
//
|
|
296
|
-
|
|
317
|
+
getPaletteForColor,
|
|
318
|
+
(s) => getTone(s.isDark ? 80 : 40)(s),
|
|
319
|
+
// Main color: lighter in dark mode, darker in light mode
|
|
320
|
+
true
|
|
321
|
+
// background
|
|
297
322
|
);
|
|
298
323
|
const onColorDynamicColor = new DynamicColor(
|
|
299
324
|
`on${upperFirst(colorname)}`,
|
|
300
|
-
|
|
301
|
-
(s) => s.isDark ? 20 : 100,
|
|
302
|
-
//
|
|
325
|
+
getPaletteForColor,
|
|
326
|
+
(s) => getTone(s.isDark ? 20 : 100)(s),
|
|
327
|
+
// Text on main color: high contrast (dark on light, light on dark)
|
|
303
328
|
false
|
|
304
329
|
);
|
|
305
330
|
const containerDynamicColor = new DynamicColor(
|
|
306
331
|
`${colorname}Container`,
|
|
307
|
-
|
|
308
|
-
(s) => s.isDark ? 30 : 90,
|
|
309
|
-
//
|
|
310
|
-
|
|
332
|
+
getPaletteForColor,
|
|
333
|
+
(s) => getTone(s.isDark ? 30 : 90)(s),
|
|
334
|
+
// Container: subtle variant (darker in dark mode, lighter in light mode)
|
|
335
|
+
true
|
|
336
|
+
// background
|
|
311
337
|
);
|
|
312
338
|
const onContainerDynamicColor = new DynamicColor(
|
|
313
339
|
`on${upperFirst(colorname)}Container`,
|
|
314
|
-
|
|
315
|
-
(s) => s.isDark ? 90 : 30,
|
|
316
|
-
//
|
|
340
|
+
getPaletteForColor,
|
|
341
|
+
(s) => getTone(s.isDark ? 90 : 30)(s),
|
|
342
|
+
// Text on container: high contrast against container background
|
|
317
343
|
false
|
|
318
344
|
);
|
|
319
345
|
customVars[colorname] = colorDynamicColor.getArgb(scheme);
|
|
@@ -328,9 +354,17 @@ var cssVar = (colorName, colorValue) => {
|
|
|
328
354
|
const value = hexFromArgb2(colorValue);
|
|
329
355
|
return `${name}:${value};`;
|
|
330
356
|
};
|
|
331
|
-
var generateTonalPaletteVars = (paletteName, palette) => {
|
|
357
|
+
var generateTonalPaletteVars = (paletteName, palette, scheme, applyContrast = false) => {
|
|
332
358
|
return STANDARD_TONES.map((tone) => {
|
|
333
|
-
|
|
359
|
+
let toneToUse = tone;
|
|
360
|
+
if (applyContrast && scheme) {
|
|
361
|
+
toneToUse = adjustToneForContrast(
|
|
362
|
+
tone,
|
|
363
|
+
scheme.contrastLevel,
|
|
364
|
+
scheme.isDark
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const color = palette.tone(toneToUse);
|
|
334
368
|
return cssVar(`${paletteName}-${tone}`, color);
|
|
335
369
|
}).join(" ");
|
|
336
370
|
};
|
|
@@ -366,7 +400,8 @@ function generateCss({
|
|
|
366
400
|
neutralVariant,
|
|
367
401
|
error,
|
|
368
402
|
colorMatch = DEFAULT_COLOR_MATCH,
|
|
369
|
-
customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS
|
|
403
|
+
customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS,
|
|
404
|
+
contrastAllColors = DEFAULT_CONTRAST_ALL_COLORS
|
|
370
405
|
}) {
|
|
371
406
|
const sourceArgb = argbFromHex(hexSource);
|
|
372
407
|
const effectiveSource = primary || hexSource;
|
|
@@ -449,36 +484,70 @@ function generateCss({
|
|
|
449
484
|
}
|
|
450
485
|
const customColors = definedColors.filter((c) => !c.core).map((c) => ({
|
|
451
486
|
name: c.name,
|
|
452
|
-
blend: c.blend ??
|
|
487
|
+
blend: c.blend ?? DEFAULT_BLEND,
|
|
453
488
|
value: argbFromHex(c.hex)
|
|
454
489
|
}));
|
|
455
490
|
const mergedColorsLight = mergeBaseAndCustomColors(
|
|
456
491
|
lightScheme,
|
|
457
492
|
customColors,
|
|
458
|
-
colorPalettes
|
|
493
|
+
colorPalettes,
|
|
494
|
+
contrastAllColors
|
|
459
495
|
);
|
|
460
496
|
const mergedColorsDark = mergeBaseAndCustomColors(
|
|
461
497
|
darkScheme,
|
|
462
498
|
customColors,
|
|
463
|
-
colorPalettes
|
|
499
|
+
colorPalettes,
|
|
500
|
+
contrastAllColors
|
|
464
501
|
);
|
|
465
502
|
const lightVars = toCssVars(mergedColorsLight);
|
|
466
503
|
const darkVars = toCssVars(mergedColorsDark);
|
|
467
504
|
const allTonalVars = [
|
|
468
505
|
// Core colors from the scheme
|
|
469
|
-
generateTonalPaletteVars(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
506
|
+
generateTonalPaletteVars(
|
|
507
|
+
"primary",
|
|
508
|
+
lightScheme.primaryPalette,
|
|
509
|
+
lightScheme,
|
|
510
|
+
contrastAllColors
|
|
511
|
+
),
|
|
512
|
+
generateTonalPaletteVars(
|
|
513
|
+
"secondary",
|
|
514
|
+
lightScheme.secondaryPalette,
|
|
515
|
+
lightScheme,
|
|
516
|
+
contrastAllColors
|
|
517
|
+
),
|
|
518
|
+
generateTonalPaletteVars(
|
|
519
|
+
"tertiary",
|
|
520
|
+
lightScheme.tertiaryPalette,
|
|
521
|
+
lightScheme,
|
|
522
|
+
contrastAllColors
|
|
523
|
+
),
|
|
524
|
+
generateTonalPaletteVars(
|
|
525
|
+
"error",
|
|
526
|
+
lightScheme.errorPalette,
|
|
527
|
+
lightScheme,
|
|
528
|
+
contrastAllColors
|
|
529
|
+
),
|
|
530
|
+
generateTonalPaletteVars(
|
|
531
|
+
"neutral",
|
|
532
|
+
lightScheme.neutralPalette,
|
|
533
|
+
lightScheme,
|
|
534
|
+
contrastAllColors
|
|
535
|
+
),
|
|
474
536
|
generateTonalPaletteVars(
|
|
475
537
|
"neutral-variant",
|
|
476
|
-
lightScheme.neutralVariantPalette
|
|
538
|
+
lightScheme.neutralVariantPalette,
|
|
539
|
+
lightScheme,
|
|
540
|
+
contrastAllColors
|
|
477
541
|
),
|
|
478
542
|
// Custom colors from our unified palette map
|
|
479
543
|
...customColors.map((customColorObj) => {
|
|
480
544
|
const palette = getPalette(colorPalettes, customColorObj.name);
|
|
481
|
-
return generateTonalPaletteVars(
|
|
545
|
+
return generateTonalPaletteVars(
|
|
546
|
+
kebabCase(customColorObj.name),
|
|
547
|
+
palette,
|
|
548
|
+
lightScheme,
|
|
549
|
+
contrastAllColors
|
|
550
|
+
);
|
|
482
551
|
})
|
|
483
552
|
].join(" ");
|
|
484
553
|
return {
|