material-theme-builder 5.0.0 → 5.1.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 CHANGED
@@ -2,7 +2,7 @@
2
2
  [![](https://img.shields.io/badge/chromatic-171c23.svg?logo=chromatic)](https://www.chromatic.com/library?appId=695eb517cb602e59b4cc045c&branch=main)
3
3
  [![](https://img.shields.io/badge/storybook-171c23.svg?logo=storybook)](https://main--695eb517cb602e59b4cc045c.chromatic.com)
4
4
 
5
- Outputs [m3 colors](https://m3.material.io/styles/color/system/overview) `--md-sys-color-*` and `--md-ref-palette-*`, 1:1 with [Material Theme Builder](https://material-foundation.github.io/material-theme-builder/), either:
5
+ Outputs [m3 colors](https://m3.material.io/styles/color/system/overview) `--md-sys-color-*`, 1:1 with [Material Theme Builder](https://material-foundation.github.io/material-theme-builder/), and the `--md-ref-palette-*` tonal palettes they are drawn from, either:
6
6
 
7
7
  - [programmatically](#programmatic-api)
8
8
  - from [CLI](#cli)
@@ -19,8 +19,8 @@ Support for:
19
19
  - [x] core-colors overrides: primary, secondary, tertiary, error, neutral,
20
20
  neutralVariant
21
21
  - [x] custom-colors (aka. "Extended colors")
22
- - [x] Harmonization (aka. `blend`) -- with effective color: `source` or
23
- `primary` if defined
22
+ - [x] Harmonization (aka. `blend`, on by default) -- with effective color:
23
+ `source` or `primary` if defined
24
24
  - [x] Shades (aka. "tonals")
25
25
  - [ ] colorMatch
26
26
 
@@ -52,6 +52,18 @@ theme.toShadcnAliases();
52
52
  theme.toShadcnRegistryItem({ fallback: true });
53
53
  ```
54
54
 
55
+ > [!NOTE]
56
+ >
57
+ > `toJson()` is shaped like Material Theme Builder's JSON export, and its
58
+ > `schemes` match it. Its `palettes`, though, are the reference palettes: the
59
+ > palettes the system colors are drawn from, the same ones, tones and values as
60
+ > the `--md-ref-palette-*` of `toCss()` (`error` and custom colors included, 28
61
+ > tones). Material Theme Builder's own export holds other palettes -- five, at
62
+ > 18 tones, which ignore the scheme variant and are not the palettes its
63
+ > `schemes` come from. They are not reproduced: Material Theme Builder's
64
+ > maintainers say they are only there as reference. See [ADR 0001](docs/adr/0001-reference-palettes-are-the-scheme-palettes.md)
65
+ > for why.
66
+
55
67
  ## CLI
56
68
 
57
69
  ```sh
package/dist/cli.js CHANGED
@@ -27,19 +27,17 @@ import {
27
27
  SchemeNeutral,
28
28
  SchemeTonalSpot,
29
29
  SchemeVibrant,
30
- TonalPalette as TonalPalette2
30
+ TonalPalette
31
31
  } from "@material/material-color-utilities";
32
32
  import { kebabCase as kebabCase4, upperFirst as upperFirst2 } from "lodash-es";
33
33
 
34
34
  // src/lib/builder.css.ts
35
- import {
36
- hexFromArgb
37
- } from "@material/material-color-utilities";
35
+ import { hexFromArgb } from "@material/material-color-utilities";
38
36
  import { kebabCase } from "lodash-es";
39
37
  function buildCss(ctx) {
40
38
  const {
41
39
  prefix,
42
- allPalettes,
40
+ refPalettes,
43
41
  mergedColorsLight,
44
42
  mergedColorsDark,
45
43
  tokenToPalette,
@@ -47,10 +45,9 @@ function buildCss(ctx) {
47
45
  } = ctx;
48
46
  function buildRefPaletteLookup() {
49
47
  const lookup = {};
50
- for (const [name, palette] of Object.entries(allPalettes)) {
51
- const paletteName = kebabCase(name);
52
- for (const tone of STANDARD_TONES) {
53
- const hex = hexFromArgb(palette.tone(tone));
48
+ for (const [paletteName, tones] of Object.entries(refPalettes)) {
49
+ for (const { tone, argb } of tones) {
50
+ const hex = hexFromArgb(argb);
54
51
  if (!lookup[hex]) lookup[hex] = [];
55
52
  lookup[hex].push({ paletteName, tone });
56
53
  }
@@ -78,15 +75,12 @@ function buildCss(ctx) {
78
75
  const value = hexFromArgb(colorValue);
79
76
  return `${name}:${value};`;
80
77
  }
81
- function generateTonalPaletteVars(paletteName, palette) {
82
- return STANDARD_TONES.map((tone) => {
83
- const color = palette.tone(tone);
84
- return refPaletteVar(paletteName, tone, color);
85
- }).join(" ");
78
+ function generateTonalPaletteVars(paletteName, tones) {
79
+ return tones.map(({ tone, argb }) => refPaletteVar(paletteName, tone, argb)).join(" ");
86
80
  }
87
81
  function generateTonalVars() {
88
- return Object.entries(allPalettes).map(
89
- ([name, palette]) => generateTonalPaletteVars(kebabCase(name), palette)
82
+ return Object.entries(refPalettes).map(
83
+ ([paletteName, tones]) => generateTonalPaletteVars(paletteName, tones)
90
84
  ).join(" ");
91
85
  }
92
86
  const refPaletteLookup = buildRefPaletteLookup();
@@ -388,29 +382,8 @@ import {
388
382
  DynamicScheme,
389
383
  Hct,
390
384
  hexFromArgb as hexFromArgb4,
391
- MaterialDynamicColors,
392
- TonalPalette
385
+ MaterialDynamicColors
393
386
  } from "@material/material-color-utilities";
394
- var MTB_TONES = [
395
- 0,
396
- 5,
397
- 10,
398
- 15,
399
- 20,
400
- 25,
401
- 30,
402
- 35,
403
- 40,
404
- 50,
405
- 60,
406
- 70,
407
- 80,
408
- 90,
409
- 95,
410
- 98,
411
- 99,
412
- 100
413
- ];
414
387
  var FIXTURE_TOKEN_ORDER = [
415
388
  "primary",
416
389
  "surfaceTint",
@@ -465,7 +438,6 @@ var FIXTURE_TOKEN_ORDER = [
465
438
  function buildJson(ctx) {
466
439
  const {
467
440
  hexSource,
468
- sourceHct,
469
441
  effectiveSourceArgb,
470
442
  primaryHct,
471
443
  SchemeClass,
@@ -476,34 +448,16 @@ function buildJson(ctx) {
476
448
  error,
477
449
  neutral,
478
450
  neutralVariant,
479
- hexCustomColors
451
+ hexCustomColors,
452
+ refPalettes
480
453
  } = ctx;
481
- const neuHct = neutral ? Hct.fromInt(argbFromHex(neutral)) : sourceHct;
482
- const nvHct = neutralVariant ? Hct.fromInt(argbFromHex(neutralVariant)) : sourceHct;
483
- const rawPalettes = {
484
- primary: TonalPalette.fromInt(effectiveSourceArgb),
485
- secondary: secondary ? TonalPalette.fromInt(argbFromHex(secondary)) : TonalPalette.fromHueAndChroma(sourceHct.hue, sourceHct.chroma / 3),
486
- tertiary: tertiary ? TonalPalette.fromInt(argbFromHex(tertiary)) : TonalPalette.fromHueAndChroma(
487
- (sourceHct.hue + 60) % 360,
488
- sourceHct.chroma / 2
489
- ),
490
- neutral: TonalPalette.fromHueAndChroma(
491
- neuHct.hue,
492
- Math.min(neuHct.chroma / 12, 4)
493
- ),
494
- "neutral-variant": TonalPalette.fromHueAndChroma(
495
- nvHct.hue,
496
- Math.min(nvHct.chroma / 6, 8)
497
- )
498
- };
499
454
  function buildJsonSchemes() {
500
- function extractSchemeColors(scheme2, backgroundScheme) {
455
+ function extractSchemeColors(scheme2) {
501
456
  const colors = {};
502
457
  for (const tokenName of FIXTURE_TOKEN_ORDER) {
503
458
  const dynamicColor = MaterialDynamicColors[tokenName];
504
- const useScheme = backgroundScheme && (tokenName === "background" || tokenName === "onBackground") ? backgroundScheme : scheme2;
505
459
  colors[tokenName] = hexFromArgb4(
506
- dynamicColor.getArgb(useScheme)
460
+ dynamicColor.getArgb(scheme2)
507
461
  ).toUpperCase();
508
462
  }
509
463
  return colors;
@@ -543,26 +497,18 @@ function buildJson(ctx) {
543
497
  neutralVariantPalette: nvPalette || baseScheme.neutralVariantPalette
544
498
  });
545
499
  if (errPalette) composedScheme.errorPalette = errPalette;
546
- jsonSchemes[name] = extractSchemeColors(composedScheme, baseScheme);
500
+ jsonSchemes[name] = extractSchemeColors(composedScheme);
547
501
  }
548
502
  return jsonSchemes;
549
503
  }
550
- function rawPalettesToJson() {
504
+ function refPalettesToJson() {
551
505
  const jsonPalettes = {};
552
- const RAW_PALETTE_NAMES = [
553
- "primary",
554
- "secondary",
555
- "tertiary",
556
- "neutral",
557
- "neutral-variant"
558
- ];
559
- for (const name of RAW_PALETTE_NAMES) {
560
- const palette = rawPalettes[name];
561
- const tones = {};
562
- for (const tone of MTB_TONES) {
563
- tones[tone.toString()] = hexFromArgb4(palette.tone(tone)).toUpperCase();
506
+ for (const [name, tones] of Object.entries(refPalettes)) {
507
+ const jsonTones = {};
508
+ for (const { tone, argb } of tones) {
509
+ jsonTones[tone.toString()] = hexFromArgb4(argb).toUpperCase();
564
510
  }
565
- jsonPalettes[name] = tones;
511
+ jsonPalettes[name] = jsonTones;
566
512
  }
567
513
  return jsonPalettes;
568
514
  }
@@ -589,14 +535,14 @@ function buildJson(ctx) {
589
535
  name: c.name,
590
536
  color: c.hex.toUpperCase(),
591
537
  description: "",
592
- harmonized: c.blend ?? DEFAULT_BLEND
538
+ harmonized: c.blend
593
539
  }));
594
540
  return {
595
541
  seed,
596
542
  coreColors,
597
543
  extendedColors,
598
544
  schemes: buildJsonSchemes(),
599
- palettes: rawPalettesToJson()
545
+ palettes: refPalettesToJson()
600
546
  };
601
547
  }
602
548
 
@@ -945,51 +891,53 @@ function getPalette(palettes, colorName) {
945
891
  }
946
892
  return palette;
947
893
  }
948
- function mergeBaseAndCustomColors(scheme, customColors, colorPalettes) {
949
- const baseVars = toRecord(tokenNames, (tokenName) => {
950
- const dynamicColor = MaterialDynamicColors2[tokenName];
951
- const argb = dynamicColor.getArgb(scheme);
952
- return [tokenName, argb];
953
- });
954
- const customVars = {};
955
- customColors.forEach((color) => {
956
- const colorname = color.name;
957
- const getPaletteForColor = () => getPalette(colorPalettes, colorname);
958
- const colorDynamicColor = new DynamicColor(
894
+ function buildCustomColorRoles(colorname, palette) {
895
+ const getPaletteForColor = () => palette;
896
+ return {
897
+ [colorname]: new DynamicColor(
959
898
  colorname,
960
899
  getPaletteForColor,
961
900
  (s) => s.isDark ? 80 : 40,
962
901
  // Main color: lighter in dark mode, darker in light mode
963
902
  true
964
903
  // background
965
- );
966
- const onColorDynamicColor = new DynamicColor(
904
+ ),
905
+ [`on${upperFirst2(colorname)}`]: new DynamicColor(
967
906
  `on${upperFirst2(colorname)}`,
968
907
  getPaletteForColor,
969
908
  (s) => s.isDark ? 20 : 100,
970
909
  // Text on main color: high contrast (dark on light, light on dark)
971
910
  false
972
- );
973
- const containerDynamicColor = new DynamicColor(
911
+ ),
912
+ [`${colorname}Container`]: new DynamicColor(
974
913
  `${colorname}Container`,
975
914
  getPaletteForColor,
976
915
  (s) => s.isDark ? 30 : 90,
977
916
  // Container: subtle variant (darker in dark mode, lighter in light mode)
978
917
  true
979
918
  // background
980
- );
981
- const onContainerDynamicColor = new DynamicColor(
919
+ ),
920
+ [`on${upperFirst2(colorname)}Container`]: new DynamicColor(
982
921
  `on${upperFirst2(colorname)}Container`,
983
922
  getPaletteForColor,
984
923
  (s) => s.isDark ? 90 : 30,
985
924
  // Text on container: high contrast against container background
986
925
  false
987
- );
988
- customVars[colorname] = colorDynamicColor.getArgb(scheme);
989
- customVars[`on${upperFirst2(colorname)}`] = onColorDynamicColor.getArgb(scheme);
990
- customVars[`${colorname}Container`] = containerDynamicColor.getArgb(scheme);
991
- customVars[`on${upperFirst2(colorname)}Container`] = onContainerDynamicColor.getArgb(scheme);
926
+ )
927
+ };
928
+ }
929
+ function mergeBaseAndCustomColors(scheme, customColorRoles) {
930
+ const baseVars = toRecord(tokenNames, (tokenName) => {
931
+ const dynamicColor = MaterialDynamicColors2[tokenName];
932
+ const argb = dynamicColor.getArgb(scheme);
933
+ return [tokenName, argb];
992
934
  });
935
+ const customVars = {};
936
+ for (const roles of Object.values(customColorRoles)) {
937
+ for (const [tokenName, dynamicColor] of Object.entries(roles)) {
938
+ customVars[tokenName] = dynamicColor.getArgb(scheme);
939
+ }
940
+ }
993
941
  return { ...baseVars, ...customVars };
994
942
  }
995
943
  function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonization) {
@@ -1008,7 +956,17 @@ function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonizatio
1008
956
  } else {
1009
957
  targetChroma = baseScheme.primaryPalette.chroma;
1010
958
  }
1011
- return TonalPalette2.fromHueAndChroma(hct.hue, targetChroma);
959
+ return TonalPalette.fromHueAndChroma(hct.hue, targetChroma);
960
+ }
961
+ function buildRefPalettes(allPalettes) {
962
+ const refPalettes = {};
963
+ for (const [name, palette] of Object.entries(allPalettes)) {
964
+ refPalettes[kebabCase4(name)] = STANDARD_TONES.map((tone) => ({
965
+ tone,
966
+ argb: palette.tone(tone)
967
+ }));
968
+ }
969
+ return refPalettes;
1012
970
  }
1013
971
  function buildTokenToPaletteMap(schemePalettes, scheme) {
1014
972
  const result = {};
@@ -1037,7 +995,7 @@ function builder(hexSource, {
1037
995
  neutral,
1038
996
  neutralVariant,
1039
997
  error,
1040
- customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS,
998
+ customColors: customColorInputs = DEFAULT_CUSTOM_COLORS,
1041
999
  prefix = DEFAULT_PREFIX
1042
1000
  } = {}) {
1043
1001
  const cores = {
@@ -1048,9 +1006,12 @@ function builder(hexSource, {
1048
1006
  neutral: optionalHex(neutral),
1049
1007
  neutralVariant: optionalHex(neutralVariant)
1050
1008
  };
1051
- assertHexInputs(hexSource, cores, hexCustomColors);
1009
+ assertHexInputs(hexSource, cores, customColorInputs);
1010
+ const hexCustomColors = customColorInputs.map((c) => ({
1011
+ ...c,
1012
+ blend: c.blend ?? DEFAULT_BLEND
1013
+ }));
1052
1014
  const sourceArgb = argbFromHex2(hexSource);
1053
- const sourceHct = Hct2.fromInt(sourceArgb);
1054
1015
  const effectiveSource = cores.primary || hexSource;
1055
1016
  const effectiveSourceArgb = argbFromHex2(effectiveSource);
1056
1017
  const effectiveSourceForHarmonization = cores.primary ? argbFromHex2(cores.primary) : sourceArgb;
@@ -1141,18 +1102,21 @@ function builder(hexSource, {
1141
1102
  };
1142
1103
  const customColors = definedColors.filter((c) => !c.core).map((c) => ({
1143
1104
  name: c.name,
1144
- blend: c.blend ?? DEFAULT_BLEND,
1145
1105
  value: argbFromHex2(c.hex)
1146
1106
  }));
1107
+ const customColorRoles = Object.fromEntries(
1108
+ customColors.map((color) => [
1109
+ color.name,
1110
+ buildCustomColorRoles(color.name, getPalette(colorPalettes, color.name))
1111
+ ])
1112
+ );
1147
1113
  const mergedColorsLight = mergeBaseAndCustomColors(
1148
1114
  lightScheme,
1149
- customColors,
1150
- colorPalettes
1115
+ customColorRoles
1151
1116
  );
1152
1117
  const mergedColorsDark = mergeBaseAndCustomColors(
1153
1118
  darkScheme,
1154
- customColors,
1155
- colorPalettes
1119
+ customColorRoles
1156
1120
  );
1157
1121
  const schemePalettes = [
1158
1122
  ["primary", lightScheme.primaryPalette],
@@ -1163,6 +1127,7 @@ function builder(hexSource, {
1163
1127
  ["neutral-variant", lightScheme.neutralVariantPalette]
1164
1128
  ];
1165
1129
  const tokenToPalette = buildTokenToPaletteMap(schemePalettes, lightScheme);
1130
+ const refPalettes = buildRefPalettes(allPalettes);
1166
1131
  const allPaletteNamesKebab = new Set(Object.keys(allPalettes).map(kebabCase4));
1167
1132
  const ctx = {
1168
1133
  hexSource,
@@ -1175,11 +1140,11 @@ function builder(hexSource, {
1175
1140
  neutralVariant,
1176
1141
  error,
1177
1142
  hexCustomColors,
1178
- sourceHct,
1179
1143
  effectiveSourceArgb,
1180
1144
  primaryHct,
1181
1145
  SchemeClass,
1182
1146
  allPalettes,
1147
+ refPalettes,
1183
1148
  mergedColorsLight,
1184
1149
  mergedColorsDark,
1185
1150
  tokenToPalette,
@@ -1197,7 +1162,8 @@ function builder(hexSource, {
1197
1162
  toFlutter: () => buildFlutter(ctx),
1198
1163
  mergedColorsLight,
1199
1164
  mergedColorsDark,
1200
- allPalettes
1165
+ allPalettes,
1166
+ customColorRoles
1201
1167
  };
1202
1168
  }
1203
1169
 
@@ -1310,7 +1276,8 @@ var customColorSchema = z.array(
1310
1276
  isHexColor,
1311
1277
  "must be a hex color \u2014 3, 6 or 8 hex digits, with or without '#' (e.g. #FF5733)"
1312
1278
  ),
1313
- blend: z.boolean().default(DEFAULT_BLEND)
1279
+ // left to builder(), which applies DEFAULT_BLEND to an omitted `blend`
1280
+ blend: z.boolean().optional()
1314
1281
  })
1315
1282
  );
1316
1283
  function writeFigmaTokens(theme, outputDir) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CustomColor, TonalPalette } from '@material/material-color-utilities';
1
+ import { CustomColor, TonalPalette, DynamicColor } from '@material/material-color-utilities';
2
2
 
3
3
  /**
4
4
  * shadcn CSS variable → M3 sys-color token mapping.
@@ -97,8 +97,13 @@ type TailwindOptions = {
97
97
  };
98
98
 
99
99
  /** A custom color defined with a hex string instead of an ARGB integer. */
100
- type HexCustomColor = Omit<CustomColor, "value"> & {
100
+ type HexCustomColor = Omit<CustomColor, "value" | "blend"> & {
101
101
  hex: string;
102
+ /**
103
+ * Harmonize the color with the source (or `primary`, when given) -- "Harmonize"
104
+ * in Material Theme Builder. Default: `DEFAULT_BLEND` (true).
105
+ */
106
+ blend?: boolean;
102
107
  };
103
108
  /** Available Material You color scheme variants. */
104
109
  declare const schemeNames: readonly ["tonalSpot", "monochrome", "neutral", "vibrant", "expressive", "fidelity", "content"];
@@ -164,7 +169,7 @@ type McuConfig = MtbConfig;
164
169
  * const json = theme.toJson();
165
170
  * ```
166
171
  */
167
- declare function builder(hexSource: MtbConfig["source"], { scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, customColors: hexCustomColors, prefix, }?: Omit<MtbConfig, "source">): {
172
+ declare function builder(hexSource: MtbConfig["source"], { scheme, contrast, primary, secondary, tertiary, neutral, neutralVariant, error, customColors: customColorInputs, prefix, }?: Omit<MtbConfig, "source">): {
168
173
  toCss: () => string;
169
174
  toJson: () => {
170
175
  seed: string;
@@ -222,6 +227,11 @@ declare function builder(hexSource: MtbConfig["source"], { scheme, contrast, pri
222
227
  neutral: TonalPalette;
223
228
  "neutral-variant": TonalPalette;
224
229
  };
230
+ customColorRoles: {
231
+ [k: string]: {
232
+ [x: string]: DynamicColor;
233
+ };
234
+ };
225
235
  };
226
236
 
227
237
  export { type McuConfig, type MtbConfig, type ShadcnRegistryItem, type ShadcnTheme, type ShadcnVarName, builder };
package/dist/index.js CHANGED
@@ -13,19 +13,17 @@ import {
13
13
  SchemeNeutral,
14
14
  SchemeTonalSpot,
15
15
  SchemeVibrant,
16
- TonalPalette as TonalPalette2
16
+ TonalPalette
17
17
  } from "@material/material-color-utilities";
18
18
  import { kebabCase as kebabCase4, upperFirst as upperFirst2 } from "lodash-es";
19
19
 
20
20
  // src/lib/builder.css.ts
21
- import {
22
- hexFromArgb
23
- } from "@material/material-color-utilities";
21
+ import { hexFromArgb } from "@material/material-color-utilities";
24
22
  import { kebabCase } from "lodash-es";
25
23
  function buildCss(ctx) {
26
24
  const {
27
25
  prefix,
28
- allPalettes,
26
+ refPalettes,
29
27
  mergedColorsLight,
30
28
  mergedColorsDark,
31
29
  tokenToPalette,
@@ -33,10 +31,9 @@ function buildCss(ctx) {
33
31
  } = ctx;
34
32
  function buildRefPaletteLookup() {
35
33
  const lookup = {};
36
- for (const [name, palette] of Object.entries(allPalettes)) {
37
- const paletteName = kebabCase(name);
38
- for (const tone of STANDARD_TONES) {
39
- const hex = hexFromArgb(palette.tone(tone));
34
+ for (const [paletteName, tones] of Object.entries(refPalettes)) {
35
+ for (const { tone, argb } of tones) {
36
+ const hex = hexFromArgb(argb);
40
37
  if (!lookup[hex]) lookup[hex] = [];
41
38
  lookup[hex].push({ paletteName, tone });
42
39
  }
@@ -64,15 +61,12 @@ function buildCss(ctx) {
64
61
  const value = hexFromArgb(colorValue);
65
62
  return `${name}:${value};`;
66
63
  }
67
- function generateTonalPaletteVars(paletteName, palette) {
68
- return STANDARD_TONES.map((tone) => {
69
- const color = palette.tone(tone);
70
- return refPaletteVar(paletteName, tone, color);
71
- }).join(" ");
64
+ function generateTonalPaletteVars(paletteName, tones) {
65
+ return tones.map(({ tone, argb }) => refPaletteVar(paletteName, tone, argb)).join(" ");
72
66
  }
73
67
  function generateTonalVars() {
74
- return Object.entries(allPalettes).map(
75
- ([name, palette]) => generateTonalPaletteVars(kebabCase(name), palette)
68
+ return Object.entries(refPalettes).map(
69
+ ([paletteName, tones]) => generateTonalPaletteVars(paletteName, tones)
76
70
  ).join(" ");
77
71
  }
78
72
  const refPaletteLookup = buildRefPaletteLookup();
@@ -374,29 +368,8 @@ import {
374
368
  DynamicScheme,
375
369
  Hct,
376
370
  hexFromArgb as hexFromArgb4,
377
- MaterialDynamicColors,
378
- TonalPalette
371
+ MaterialDynamicColors
379
372
  } from "@material/material-color-utilities";
380
- var MTB_TONES = [
381
- 0,
382
- 5,
383
- 10,
384
- 15,
385
- 20,
386
- 25,
387
- 30,
388
- 35,
389
- 40,
390
- 50,
391
- 60,
392
- 70,
393
- 80,
394
- 90,
395
- 95,
396
- 98,
397
- 99,
398
- 100
399
- ];
400
373
  var FIXTURE_TOKEN_ORDER = [
401
374
  "primary",
402
375
  "surfaceTint",
@@ -451,7 +424,6 @@ var FIXTURE_TOKEN_ORDER = [
451
424
  function buildJson(ctx) {
452
425
  const {
453
426
  hexSource,
454
- sourceHct,
455
427
  effectiveSourceArgb,
456
428
  primaryHct,
457
429
  SchemeClass,
@@ -462,34 +434,16 @@ function buildJson(ctx) {
462
434
  error,
463
435
  neutral,
464
436
  neutralVariant,
465
- hexCustomColors
437
+ hexCustomColors,
438
+ refPalettes
466
439
  } = ctx;
467
- const neuHct = neutral ? Hct.fromInt(argbFromHex(neutral)) : sourceHct;
468
- const nvHct = neutralVariant ? Hct.fromInt(argbFromHex(neutralVariant)) : sourceHct;
469
- const rawPalettes = {
470
- primary: TonalPalette.fromInt(effectiveSourceArgb),
471
- secondary: secondary ? TonalPalette.fromInt(argbFromHex(secondary)) : TonalPalette.fromHueAndChroma(sourceHct.hue, sourceHct.chroma / 3),
472
- tertiary: tertiary ? TonalPalette.fromInt(argbFromHex(tertiary)) : TonalPalette.fromHueAndChroma(
473
- (sourceHct.hue + 60) % 360,
474
- sourceHct.chroma / 2
475
- ),
476
- neutral: TonalPalette.fromHueAndChroma(
477
- neuHct.hue,
478
- Math.min(neuHct.chroma / 12, 4)
479
- ),
480
- "neutral-variant": TonalPalette.fromHueAndChroma(
481
- nvHct.hue,
482
- Math.min(nvHct.chroma / 6, 8)
483
- )
484
- };
485
440
  function buildJsonSchemes() {
486
- function extractSchemeColors(scheme2, backgroundScheme) {
441
+ function extractSchemeColors(scheme2) {
487
442
  const colors = {};
488
443
  for (const tokenName of FIXTURE_TOKEN_ORDER) {
489
444
  const dynamicColor = MaterialDynamicColors[tokenName];
490
- const useScheme = backgroundScheme && (tokenName === "background" || tokenName === "onBackground") ? backgroundScheme : scheme2;
491
445
  colors[tokenName] = hexFromArgb4(
492
- dynamicColor.getArgb(useScheme)
446
+ dynamicColor.getArgb(scheme2)
493
447
  ).toUpperCase();
494
448
  }
495
449
  return colors;
@@ -529,26 +483,18 @@ function buildJson(ctx) {
529
483
  neutralVariantPalette: nvPalette || baseScheme.neutralVariantPalette
530
484
  });
531
485
  if (errPalette) composedScheme.errorPalette = errPalette;
532
- jsonSchemes[name] = extractSchemeColors(composedScheme, baseScheme);
486
+ jsonSchemes[name] = extractSchemeColors(composedScheme);
533
487
  }
534
488
  return jsonSchemes;
535
489
  }
536
- function rawPalettesToJson() {
490
+ function refPalettesToJson() {
537
491
  const jsonPalettes = {};
538
- const RAW_PALETTE_NAMES = [
539
- "primary",
540
- "secondary",
541
- "tertiary",
542
- "neutral",
543
- "neutral-variant"
544
- ];
545
- for (const name of RAW_PALETTE_NAMES) {
546
- const palette = rawPalettes[name];
547
- const tones = {};
548
- for (const tone of MTB_TONES) {
549
- tones[tone.toString()] = hexFromArgb4(palette.tone(tone)).toUpperCase();
492
+ for (const [name, tones] of Object.entries(refPalettes)) {
493
+ const jsonTones = {};
494
+ for (const { tone, argb } of tones) {
495
+ jsonTones[tone.toString()] = hexFromArgb4(argb).toUpperCase();
550
496
  }
551
- jsonPalettes[name] = tones;
497
+ jsonPalettes[name] = jsonTones;
552
498
  }
553
499
  return jsonPalettes;
554
500
  }
@@ -575,14 +521,14 @@ function buildJson(ctx) {
575
521
  name: c.name,
576
522
  color: c.hex.toUpperCase(),
577
523
  description: "",
578
- harmonized: c.blend ?? DEFAULT_BLEND
524
+ harmonized: c.blend
579
525
  }));
580
526
  return {
581
527
  seed,
582
528
  coreColors,
583
529
  extendedColors,
584
530
  schemes: buildJsonSchemes(),
585
- palettes: rawPalettesToJson()
531
+ palettes: refPalettesToJson()
586
532
  };
587
533
  }
588
534
 
@@ -922,51 +868,53 @@ function getPalette(palettes, colorName) {
922
868
  }
923
869
  return palette;
924
870
  }
925
- function mergeBaseAndCustomColors(scheme, customColors, colorPalettes) {
926
- const baseVars = toRecord(tokenNames, (tokenName) => {
927
- const dynamicColor = MaterialDynamicColors2[tokenName];
928
- const argb = dynamicColor.getArgb(scheme);
929
- return [tokenName, argb];
930
- });
931
- const customVars = {};
932
- customColors.forEach((color) => {
933
- const colorname = color.name;
934
- const getPaletteForColor = () => getPalette(colorPalettes, colorname);
935
- const colorDynamicColor = new DynamicColor(
871
+ function buildCustomColorRoles(colorname, palette) {
872
+ const getPaletteForColor = () => palette;
873
+ return {
874
+ [colorname]: new DynamicColor(
936
875
  colorname,
937
876
  getPaletteForColor,
938
877
  (s) => s.isDark ? 80 : 40,
939
878
  // Main color: lighter in dark mode, darker in light mode
940
879
  true
941
880
  // background
942
- );
943
- const onColorDynamicColor = new DynamicColor(
881
+ ),
882
+ [`on${upperFirst2(colorname)}`]: new DynamicColor(
944
883
  `on${upperFirst2(colorname)}`,
945
884
  getPaletteForColor,
946
885
  (s) => s.isDark ? 20 : 100,
947
886
  // Text on main color: high contrast (dark on light, light on dark)
948
887
  false
949
- );
950
- const containerDynamicColor = new DynamicColor(
888
+ ),
889
+ [`${colorname}Container`]: new DynamicColor(
951
890
  `${colorname}Container`,
952
891
  getPaletteForColor,
953
892
  (s) => s.isDark ? 30 : 90,
954
893
  // Container: subtle variant (darker in dark mode, lighter in light mode)
955
894
  true
956
895
  // background
957
- );
958
- const onContainerDynamicColor = new DynamicColor(
896
+ ),
897
+ [`on${upperFirst2(colorname)}Container`]: new DynamicColor(
959
898
  `on${upperFirst2(colorname)}Container`,
960
899
  getPaletteForColor,
961
900
  (s) => s.isDark ? 90 : 30,
962
901
  // Text on container: high contrast against container background
963
902
  false
964
- );
965
- customVars[colorname] = colorDynamicColor.getArgb(scheme);
966
- customVars[`on${upperFirst2(colorname)}`] = onColorDynamicColor.getArgb(scheme);
967
- customVars[`${colorname}Container`] = containerDynamicColor.getArgb(scheme);
968
- customVars[`on${upperFirst2(colorname)}Container`] = onContainerDynamicColor.getArgb(scheme);
903
+ )
904
+ };
905
+ }
906
+ function mergeBaseAndCustomColors(scheme, customColorRoles) {
907
+ const baseVars = toRecord(tokenNames, (tokenName) => {
908
+ const dynamicColor = MaterialDynamicColors2[tokenName];
909
+ const argb = dynamicColor.getArgb(scheme);
910
+ return [tokenName, argb];
969
911
  });
912
+ const customVars = {};
913
+ for (const roles of Object.values(customColorRoles)) {
914
+ for (const [tokenName, dynamicColor] of Object.entries(roles)) {
915
+ customVars[tokenName] = dynamicColor.getArgb(scheme);
916
+ }
917
+ }
970
918
  return { ...baseVars, ...customVars };
971
919
  }
972
920
  function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonization) {
@@ -985,7 +933,17 @@ function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonizatio
985
933
  } else {
986
934
  targetChroma = baseScheme.primaryPalette.chroma;
987
935
  }
988
- return TonalPalette2.fromHueAndChroma(hct.hue, targetChroma);
936
+ return TonalPalette.fromHueAndChroma(hct.hue, targetChroma);
937
+ }
938
+ function buildRefPalettes(allPalettes) {
939
+ const refPalettes = {};
940
+ for (const [name, palette] of Object.entries(allPalettes)) {
941
+ refPalettes[kebabCase4(name)] = STANDARD_TONES.map((tone) => ({
942
+ tone,
943
+ argb: palette.tone(tone)
944
+ }));
945
+ }
946
+ return refPalettes;
989
947
  }
990
948
  function buildTokenToPaletteMap(schemePalettes, scheme) {
991
949
  const result = {};
@@ -1014,7 +972,7 @@ function builder(hexSource, {
1014
972
  neutral,
1015
973
  neutralVariant,
1016
974
  error,
1017
- customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS,
975
+ customColors: customColorInputs = DEFAULT_CUSTOM_COLORS,
1018
976
  prefix = DEFAULT_PREFIX
1019
977
  } = {}) {
1020
978
  const cores = {
@@ -1025,9 +983,12 @@ function builder(hexSource, {
1025
983
  neutral: optionalHex(neutral),
1026
984
  neutralVariant: optionalHex(neutralVariant)
1027
985
  };
1028
- assertHexInputs(hexSource, cores, hexCustomColors);
986
+ assertHexInputs(hexSource, cores, customColorInputs);
987
+ const hexCustomColors = customColorInputs.map((c) => ({
988
+ ...c,
989
+ blend: c.blend ?? DEFAULT_BLEND
990
+ }));
1029
991
  const sourceArgb = argbFromHex2(hexSource);
1030
- const sourceHct = Hct2.fromInt(sourceArgb);
1031
992
  const effectiveSource = cores.primary || hexSource;
1032
993
  const effectiveSourceArgb = argbFromHex2(effectiveSource);
1033
994
  const effectiveSourceForHarmonization = cores.primary ? argbFromHex2(cores.primary) : sourceArgb;
@@ -1118,18 +1079,21 @@ function builder(hexSource, {
1118
1079
  };
1119
1080
  const customColors = definedColors.filter((c) => !c.core).map((c) => ({
1120
1081
  name: c.name,
1121
- blend: c.blend ?? DEFAULT_BLEND,
1122
1082
  value: argbFromHex2(c.hex)
1123
1083
  }));
1084
+ const customColorRoles = Object.fromEntries(
1085
+ customColors.map((color) => [
1086
+ color.name,
1087
+ buildCustomColorRoles(color.name, getPalette(colorPalettes, color.name))
1088
+ ])
1089
+ );
1124
1090
  const mergedColorsLight = mergeBaseAndCustomColors(
1125
1091
  lightScheme,
1126
- customColors,
1127
- colorPalettes
1092
+ customColorRoles
1128
1093
  );
1129
1094
  const mergedColorsDark = mergeBaseAndCustomColors(
1130
1095
  darkScheme,
1131
- customColors,
1132
- colorPalettes
1096
+ customColorRoles
1133
1097
  );
1134
1098
  const schemePalettes = [
1135
1099
  ["primary", lightScheme.primaryPalette],
@@ -1140,6 +1104,7 @@ function builder(hexSource, {
1140
1104
  ["neutral-variant", lightScheme.neutralVariantPalette]
1141
1105
  ];
1142
1106
  const tokenToPalette = buildTokenToPaletteMap(schemePalettes, lightScheme);
1107
+ const refPalettes = buildRefPalettes(allPalettes);
1143
1108
  const allPaletteNamesKebab = new Set(Object.keys(allPalettes).map(kebabCase4));
1144
1109
  const ctx = {
1145
1110
  hexSource,
@@ -1152,11 +1117,11 @@ function builder(hexSource, {
1152
1117
  neutralVariant,
1153
1118
  error,
1154
1119
  hexCustomColors,
1155
- sourceHct,
1156
1120
  effectiveSourceArgb,
1157
1121
  primaryHct,
1158
1122
  SchemeClass,
1159
1123
  allPalettes,
1124
+ refPalettes,
1160
1125
  mergedColorsLight,
1161
1126
  mergedColorsDark,
1162
1127
  tokenToPalette,
@@ -1174,7 +1139,8 @@ function builder(hexSource, {
1174
1139
  toFlutter: () => buildFlutter(ctx),
1175
1140
  mergedColorsLight,
1176
1141
  mergedColorsDark,
1177
- allPalettes
1142
+ allPalettes,
1143
+ customColorRoles
1178
1144
  };
1179
1145
  }
1180
1146
  export {
package/dist/react.d.ts CHANGED
@@ -134,8 +134,13 @@ declare const tokenDescriptions: {
134
134
  type TokenName = keyof typeof tokenDescriptions;
135
135
 
136
136
  /** A custom color defined with a hex string instead of an ARGB integer. */
137
- type HexCustomColor = Omit<CustomColor, "value"> & {
137
+ type HexCustomColor = Omit<CustomColor, "value" | "blend"> & {
138
138
  hex: string;
139
+ /**
140
+ * Harmonize the color with the source (or `primary`, when given) -- "Harmonize"
141
+ * in Material Theme Builder. Default: `DEFAULT_BLEND` (true).
142
+ */
143
+ blend?: boolean;
139
144
  };
140
145
  /** Available Material You color scheme variants. */
141
146
  declare const schemeNames: readonly ["tonalSpot", "monochrome", "neutral", "vibrant", "expressive", "fidelity", "content"];
package/dist/react.js CHANGED
@@ -3333,19 +3333,17 @@ import {
3333
3333
  SchemeNeutral,
3334
3334
  SchemeTonalSpot,
3335
3335
  SchemeVibrant,
3336
- TonalPalette as TonalPalette2
3336
+ TonalPalette
3337
3337
  } from "@material/material-color-utilities";
3338
3338
  import { kebabCase as kebabCase4, upperFirst as upperFirst2 } from "lodash-es";
3339
3339
 
3340
3340
  // src/lib/builder.css.ts
3341
- import {
3342
- hexFromArgb
3343
- } from "@material/material-color-utilities";
3341
+ import { hexFromArgb } from "@material/material-color-utilities";
3344
3342
  import { kebabCase } from "lodash-es";
3345
3343
  function buildCss(ctx) {
3346
3344
  const {
3347
3345
  prefix,
3348
- allPalettes,
3346
+ refPalettes,
3349
3347
  mergedColorsLight,
3350
3348
  mergedColorsDark,
3351
3349
  tokenToPalette,
@@ -3353,10 +3351,9 @@ function buildCss(ctx) {
3353
3351
  } = ctx;
3354
3352
  function buildRefPaletteLookup() {
3355
3353
  const lookup = {};
3356
- for (const [name, palette] of Object.entries(allPalettes)) {
3357
- const paletteName = kebabCase(name);
3358
- for (const tone of STANDARD_TONES) {
3359
- const hex = hexFromArgb(palette.tone(tone));
3354
+ for (const [paletteName, tones] of Object.entries(refPalettes)) {
3355
+ for (const { tone, argb } of tones) {
3356
+ const hex = hexFromArgb(argb);
3360
3357
  if (!lookup[hex]) lookup[hex] = [];
3361
3358
  lookup[hex].push({ paletteName, tone });
3362
3359
  }
@@ -3384,15 +3381,12 @@ function buildCss(ctx) {
3384
3381
  const value = hexFromArgb(colorValue);
3385
3382
  return `${name}:${value};`;
3386
3383
  }
3387
- function generateTonalPaletteVars(paletteName, palette) {
3388
- return STANDARD_TONES.map((tone) => {
3389
- const color = palette.tone(tone);
3390
- return refPaletteVar(paletteName, tone, color);
3391
- }).join(" ");
3384
+ function generateTonalPaletteVars(paletteName, tones) {
3385
+ return tones.map(({ tone, argb }) => refPaletteVar(paletteName, tone, argb)).join(" ");
3392
3386
  }
3393
3387
  function generateTonalVars() {
3394
- return Object.entries(allPalettes).map(
3395
- ([name, palette]) => generateTonalPaletteVars(kebabCase(name), palette)
3388
+ return Object.entries(refPalettes).map(
3389
+ ([paletteName, tones]) => generateTonalPaletteVars(paletteName, tones)
3396
3390
  ).join(" ");
3397
3391
  }
3398
3392
  const refPaletteLookup = buildRefPaletteLookup();
@@ -3694,29 +3688,8 @@ import {
3694
3688
  DynamicScheme,
3695
3689
  Hct,
3696
3690
  hexFromArgb as hexFromArgb4,
3697
- MaterialDynamicColors,
3698
- TonalPalette
3691
+ MaterialDynamicColors
3699
3692
  } from "@material/material-color-utilities";
3700
- var MTB_TONES = [
3701
- 0,
3702
- 5,
3703
- 10,
3704
- 15,
3705
- 20,
3706
- 25,
3707
- 30,
3708
- 35,
3709
- 40,
3710
- 50,
3711
- 60,
3712
- 70,
3713
- 80,
3714
- 90,
3715
- 95,
3716
- 98,
3717
- 99,
3718
- 100
3719
- ];
3720
3693
  var FIXTURE_TOKEN_ORDER = [
3721
3694
  "primary",
3722
3695
  "surfaceTint",
@@ -3771,7 +3744,6 @@ var FIXTURE_TOKEN_ORDER = [
3771
3744
  function buildJson(ctx) {
3772
3745
  const {
3773
3746
  hexSource,
3774
- sourceHct,
3775
3747
  effectiveSourceArgb,
3776
3748
  primaryHct,
3777
3749
  SchemeClass,
@@ -3782,34 +3754,16 @@ function buildJson(ctx) {
3782
3754
  error,
3783
3755
  neutral,
3784
3756
  neutralVariant,
3785
- hexCustomColors
3757
+ hexCustomColors,
3758
+ refPalettes
3786
3759
  } = ctx;
3787
- const neuHct = neutral ? Hct.fromInt(argbFromHex(neutral)) : sourceHct;
3788
- const nvHct = neutralVariant ? Hct.fromInt(argbFromHex(neutralVariant)) : sourceHct;
3789
- const rawPalettes = {
3790
- primary: TonalPalette.fromInt(effectiveSourceArgb),
3791
- secondary: secondary ? TonalPalette.fromInt(argbFromHex(secondary)) : TonalPalette.fromHueAndChroma(sourceHct.hue, sourceHct.chroma / 3),
3792
- tertiary: tertiary ? TonalPalette.fromInt(argbFromHex(tertiary)) : TonalPalette.fromHueAndChroma(
3793
- (sourceHct.hue + 60) % 360,
3794
- sourceHct.chroma / 2
3795
- ),
3796
- neutral: TonalPalette.fromHueAndChroma(
3797
- neuHct.hue,
3798
- Math.min(neuHct.chroma / 12, 4)
3799
- ),
3800
- "neutral-variant": TonalPalette.fromHueAndChroma(
3801
- nvHct.hue,
3802
- Math.min(nvHct.chroma / 6, 8)
3803
- )
3804
- };
3805
3760
  function buildJsonSchemes() {
3806
- function extractSchemeColors(scheme2, backgroundScheme) {
3761
+ function extractSchemeColors(scheme2) {
3807
3762
  const colors = {};
3808
3763
  for (const tokenName of FIXTURE_TOKEN_ORDER) {
3809
3764
  const dynamicColor = MaterialDynamicColors[tokenName];
3810
- const useScheme = backgroundScheme && (tokenName === "background" || tokenName === "onBackground") ? backgroundScheme : scheme2;
3811
3765
  colors[tokenName] = hexFromArgb4(
3812
- dynamicColor.getArgb(useScheme)
3766
+ dynamicColor.getArgb(scheme2)
3813
3767
  ).toUpperCase();
3814
3768
  }
3815
3769
  return colors;
@@ -3849,26 +3803,18 @@ function buildJson(ctx) {
3849
3803
  neutralVariantPalette: nvPalette || baseScheme.neutralVariantPalette
3850
3804
  });
3851
3805
  if (errPalette) composedScheme.errorPalette = errPalette;
3852
- jsonSchemes[name] = extractSchemeColors(composedScheme, baseScheme);
3806
+ jsonSchemes[name] = extractSchemeColors(composedScheme);
3853
3807
  }
3854
3808
  return jsonSchemes;
3855
3809
  }
3856
- function rawPalettesToJson() {
3810
+ function refPalettesToJson() {
3857
3811
  const jsonPalettes = {};
3858
- const RAW_PALETTE_NAMES = [
3859
- "primary",
3860
- "secondary",
3861
- "tertiary",
3862
- "neutral",
3863
- "neutral-variant"
3864
- ];
3865
- for (const name of RAW_PALETTE_NAMES) {
3866
- const palette = rawPalettes[name];
3867
- const tones = {};
3868
- for (const tone of MTB_TONES) {
3869
- tones[tone.toString()] = hexFromArgb4(palette.tone(tone)).toUpperCase();
3812
+ for (const [name, tones] of Object.entries(refPalettes)) {
3813
+ const jsonTones = {};
3814
+ for (const { tone, argb } of tones) {
3815
+ jsonTones[tone.toString()] = hexFromArgb4(argb).toUpperCase();
3870
3816
  }
3871
- jsonPalettes[name] = tones;
3817
+ jsonPalettes[name] = jsonTones;
3872
3818
  }
3873
3819
  return jsonPalettes;
3874
3820
  }
@@ -3895,14 +3841,14 @@ function buildJson(ctx) {
3895
3841
  name: c.name,
3896
3842
  color: c.hex.toUpperCase(),
3897
3843
  description: "",
3898
- harmonized: c.blend ?? DEFAULT_BLEND
3844
+ harmonized: c.blend
3899
3845
  }));
3900
3846
  return {
3901
3847
  seed,
3902
3848
  coreColors,
3903
3849
  extendedColors,
3904
3850
  schemes: buildJsonSchemes(),
3905
- palettes: rawPalettesToJson()
3851
+ palettes: refPalettesToJson()
3906
3852
  };
3907
3853
  }
3908
3854
 
@@ -4242,51 +4188,53 @@ function getPalette(palettes, colorName) {
4242
4188
  }
4243
4189
  return palette;
4244
4190
  }
4245
- function mergeBaseAndCustomColors(scheme, customColors, colorPalettes) {
4246
- const baseVars = toRecord(tokenNames, (tokenName) => {
4247
- const dynamicColor = MaterialDynamicColors2[tokenName];
4248
- const argb = dynamicColor.getArgb(scheme);
4249
- return [tokenName, argb];
4250
- });
4251
- const customVars = {};
4252
- customColors.forEach((color) => {
4253
- const colorname = color.name;
4254
- const getPaletteForColor = () => getPalette(colorPalettes, colorname);
4255
- const colorDynamicColor = new DynamicColor(
4191
+ function buildCustomColorRoles(colorname, palette) {
4192
+ const getPaletteForColor = () => palette;
4193
+ return {
4194
+ [colorname]: new DynamicColor(
4256
4195
  colorname,
4257
4196
  getPaletteForColor,
4258
4197
  (s) => s.isDark ? 80 : 40,
4259
4198
  // Main color: lighter in dark mode, darker in light mode
4260
4199
  true
4261
4200
  // background
4262
- );
4263
- const onColorDynamicColor = new DynamicColor(
4201
+ ),
4202
+ [`on${upperFirst2(colorname)}`]: new DynamicColor(
4264
4203
  `on${upperFirst2(colorname)}`,
4265
4204
  getPaletteForColor,
4266
4205
  (s) => s.isDark ? 20 : 100,
4267
4206
  // Text on main color: high contrast (dark on light, light on dark)
4268
4207
  false
4269
- );
4270
- const containerDynamicColor = new DynamicColor(
4208
+ ),
4209
+ [`${colorname}Container`]: new DynamicColor(
4271
4210
  `${colorname}Container`,
4272
4211
  getPaletteForColor,
4273
4212
  (s) => s.isDark ? 30 : 90,
4274
4213
  // Container: subtle variant (darker in dark mode, lighter in light mode)
4275
4214
  true
4276
4215
  // background
4277
- );
4278
- const onContainerDynamicColor = new DynamicColor(
4216
+ ),
4217
+ [`on${upperFirst2(colorname)}Container`]: new DynamicColor(
4279
4218
  `on${upperFirst2(colorname)}Container`,
4280
4219
  getPaletteForColor,
4281
4220
  (s) => s.isDark ? 90 : 30,
4282
4221
  // Text on container: high contrast against container background
4283
4222
  false
4284
- );
4285
- customVars[colorname] = colorDynamicColor.getArgb(scheme);
4286
- customVars[`on${upperFirst2(colorname)}`] = onColorDynamicColor.getArgb(scheme);
4287
- customVars[`${colorname}Container`] = containerDynamicColor.getArgb(scheme);
4288
- customVars[`on${upperFirst2(colorname)}Container`] = onContainerDynamicColor.getArgb(scheme);
4223
+ )
4224
+ };
4225
+ }
4226
+ function mergeBaseAndCustomColors(scheme, customColorRoles) {
4227
+ const baseVars = toRecord(tokenNames, (tokenName) => {
4228
+ const dynamicColor = MaterialDynamicColors2[tokenName];
4229
+ const argb = dynamicColor.getArgb(scheme);
4230
+ return [tokenName, argb];
4289
4231
  });
4232
+ const customVars = {};
4233
+ for (const roles of Object.values(customColorRoles)) {
4234
+ for (const [tokenName, dynamicColor] of Object.entries(roles)) {
4235
+ customVars[tokenName] = dynamicColor.getArgb(scheme);
4236
+ }
4237
+ }
4290
4238
  return { ...baseVars, ...customVars };
4291
4239
  }
4292
4240
  function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonization) {
@@ -4305,7 +4253,17 @@ function createColorPalette(colorDef, baseScheme, effectiveSourceForHarmonizatio
4305
4253
  } else {
4306
4254
  targetChroma = baseScheme.primaryPalette.chroma;
4307
4255
  }
4308
- return TonalPalette2.fromHueAndChroma(hct.hue, targetChroma);
4256
+ return TonalPalette.fromHueAndChroma(hct.hue, targetChroma);
4257
+ }
4258
+ function buildRefPalettes(allPalettes) {
4259
+ const refPalettes = {};
4260
+ for (const [name, palette] of Object.entries(allPalettes)) {
4261
+ refPalettes[kebabCase4(name)] = STANDARD_TONES.map((tone) => ({
4262
+ tone,
4263
+ argb: palette.tone(tone)
4264
+ }));
4265
+ }
4266
+ return refPalettes;
4309
4267
  }
4310
4268
  function buildTokenToPaletteMap(schemePalettes, scheme) {
4311
4269
  const result = {};
@@ -4334,7 +4292,7 @@ function builder(hexSource, {
4334
4292
  neutral,
4335
4293
  neutralVariant,
4336
4294
  error,
4337
- customColors: hexCustomColors = DEFAULT_CUSTOM_COLORS,
4295
+ customColors: customColorInputs = DEFAULT_CUSTOM_COLORS,
4338
4296
  prefix = DEFAULT_PREFIX
4339
4297
  } = {}) {
4340
4298
  const cores = {
@@ -4345,9 +4303,12 @@ function builder(hexSource, {
4345
4303
  neutral: optionalHex(neutral),
4346
4304
  neutralVariant: optionalHex(neutralVariant)
4347
4305
  };
4348
- assertHexInputs(hexSource, cores, hexCustomColors);
4306
+ assertHexInputs(hexSource, cores, customColorInputs);
4307
+ const hexCustomColors = customColorInputs.map((c) => ({
4308
+ ...c,
4309
+ blend: c.blend ?? DEFAULT_BLEND
4310
+ }));
4349
4311
  const sourceArgb = argbFromHex2(hexSource);
4350
- const sourceHct = Hct2.fromInt(sourceArgb);
4351
4312
  const effectiveSource = cores.primary || hexSource;
4352
4313
  const effectiveSourceArgb = argbFromHex2(effectiveSource);
4353
4314
  const effectiveSourceForHarmonization = cores.primary ? argbFromHex2(cores.primary) : sourceArgb;
@@ -4438,18 +4399,21 @@ function builder(hexSource, {
4438
4399
  };
4439
4400
  const customColors = definedColors.filter((c) => !c.core).map((c) => ({
4440
4401
  name: c.name,
4441
- blend: c.blend ?? DEFAULT_BLEND,
4442
4402
  value: argbFromHex2(c.hex)
4443
4403
  }));
4404
+ const customColorRoles = Object.fromEntries(
4405
+ customColors.map((color) => [
4406
+ color.name,
4407
+ buildCustomColorRoles(color.name, getPalette(colorPalettes, color.name))
4408
+ ])
4409
+ );
4444
4410
  const mergedColorsLight = mergeBaseAndCustomColors(
4445
4411
  lightScheme,
4446
- customColors,
4447
- colorPalettes
4412
+ customColorRoles
4448
4413
  );
4449
4414
  const mergedColorsDark = mergeBaseAndCustomColors(
4450
4415
  darkScheme,
4451
- customColors,
4452
- colorPalettes
4416
+ customColorRoles
4453
4417
  );
4454
4418
  const schemePalettes = [
4455
4419
  ["primary", lightScheme.primaryPalette],
@@ -4460,6 +4424,7 @@ function builder(hexSource, {
4460
4424
  ["neutral-variant", lightScheme.neutralVariantPalette]
4461
4425
  ];
4462
4426
  const tokenToPalette = buildTokenToPaletteMap(schemePalettes, lightScheme);
4427
+ const refPalettes = buildRefPalettes(allPalettes);
4463
4428
  const allPaletteNamesKebab = new Set(Object.keys(allPalettes).map(kebabCase4));
4464
4429
  const ctx = {
4465
4430
  hexSource,
@@ -4472,11 +4437,11 @@ function builder(hexSource, {
4472
4437
  neutralVariant,
4473
4438
  error,
4474
4439
  hexCustomColors,
4475
- sourceHct,
4476
4440
  effectiveSourceArgb,
4477
4441
  primaryHct,
4478
4442
  SchemeClass,
4479
4443
  allPalettes,
4444
+ refPalettes,
4480
4445
  mergedColorsLight,
4481
4446
  mergedColorsDark,
4482
4447
  tokenToPalette,
@@ -4494,7 +4459,8 @@ function builder(hexSource, {
4494
4459
  toFlutter: () => buildFlutter(ctx),
4495
4460
  mergedColorsLight,
4496
4461
  mergedColorsDark,
4497
- allPalettes
4462
+ allPalettes,
4463
+ customColorRoles
4498
4464
  };
4499
4465
  }
4500
4466
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "material-theme-builder",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "m3 color-system for JS/TS ecosystem",
5
5
  "keywords": [
6
6
  "react",