material-theme-builder 4.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
@@ -153,19 +165,18 @@ return (
153
165
 
154
166
  ## Tailwind
155
167
 
156
- Compatible through [theme variables](https://tailwindcss.com/docs/theme) — a
157
- stylesheet for the standard tokens, and a plugin for the custom colors:
168
+ Compatible through [theme variables](https://tailwindcss.com/docs/theme) — one
169
+ plugin, one line:
158
170
 
159
171
  ```css
160
172
  @import "tailwindcss";
161
173
 
162
- @import "material-theme-builder/tailwind.css";
163
174
  @plugin "material-theme-builder/tailwind" {
164
175
  custom-colors: myCustomColor1, myCustomColor2;
165
176
  }
166
177
  ```
167
178
 
168
- Drop the `@plugin` line if you have no custom colors.
179
+ Drop the `custom-colors` block if you have none.
169
180
 
170
181
  <details>
171
182
 
@@ -193,27 +204,16 @@ Each name listed brings its four scheme roles and eleven shades —
193
204
  > of a nested `<Mtb>`.
194
205
 
195
206
  <details>
196
- <summary>The theme variables the stylesheet declares</summary>
197
-
198
- Generated from [`toTailwind()`](#programmatic-api), so the two cannot drift:
207
+ <summary>The names it declares</summary>
199
208
 
200
- ```css
201
- @theme inline {
202
- --color-background: var(--md-sys-color-background);
203
- --color-error: var(--md-sys-color-error);
204
- --color-error-container: var(--md-sys-color-error-container);
205
- --color-inverse-on-surface: var(--md-sys-color-inverse-on-surface);
206
- --color-inverse-primary: var(--md-sys-color-inverse-primary);
207
- --color-inverse-surface: var(--md-sys-color-inverse-surface);
208
- --color-on-background: var(--md-sys-color-on-background);
209
- --color-on-error: var(--md-sys-color-on-error);
210
- /* ... */
211
- }
212
- ```
209
+ 115 standard ones — every M3 scheme token (`bg-surface-container-low`,
210
+ `text-on-primary`, `border-outline-variant`…), plus eleven Tailwind shades for
211
+ each of `primary`, `secondary`, `tertiary`, `error`, `neutral` and
212
+ `neutral-variant` (`bg-primary-300`). Then four roles and eleven shades per
213
+ custom color you name.
213
214
 
214
- 115 names in all — every M3 scheme token, plus eleven Tailwind shades for each
215
- of `primary`, `secondary`, `tertiary`, `error`, `neutral` and
216
- `neutral-variant`.
215
+ They are theme _defaults_, so an `@theme` block of your own wins over them
216
+ whatever the order. See [shadcn](#shadcn), where three names collide.
217
217
 
218
218
  </details>
219
219
 
@@ -233,9 +233,8 @@ In your
233
233
  @import "shadcn/tailwind.css";
234
234
 
235
235
  /* 👇🏻 ADD THIS 👇🏻 */
236
- @import "material-theme-builder/tailwind.css"; /* the M3 tw classNames (optional) */
237
236
  @import "material-theme-builder/shadcn.css"; /* shadcn's variables remapping on M3 */
238
- @plugin "material-theme-builder/tailwind" { /* your custom colors (optional) */
237
+ @plugin "material-theme-builder/tailwind" { /* the M3 tw classNames (optional) */
239
238
  custom-colors: myCustomColor1, myCustomColor2;
240
239
  }
241
240
  /* 👆🏻 ADD THIS 👆🏻 */
@@ -265,10 +264,10 @@ the M3 custom properties, so every shadcn component follows whichever `<Mtb>` is
265
264
  above it in the tree. It carries no colors of its own — mount an `<Mtb>`, or
266
265
  emit [`toCss()`](#programmatic-api) server-side, or nothing resolves.
267
266
 
268
- The other two are optional. They are the [Tailwind](#tailwind) recipe unchanged,
269
- and what they add is names to write yourself — `bg-surface-container-low`,
270
- `text-on-primary`, your custom colors. Drop them and every shadcn component
271
- still follows the theme.
267
+ The `@plugin` line is optional. It is the [Tailwind](#tailwind) recipe
268
+ unchanged, and what it adds is names to write yourself —
269
+ `bg-surface-container-low`, `text-on-primary`, your custom colors. Drop it and
270
+ every shadcn component still follows the theme.
272
271
 
273
272
  For the opposite trade — concrete `oklch()` values and no `var()` at all, frozen
274
273
  at build time — see [`toShadcn()`](#programmatic-api).
@@ -282,8 +281,9 @@ at build time — see [`toShadcn()`](#programmatic-api).
282
281
  > certainly do not need to care.
283
282
 
284
283
  Material and shadcn picked the same name for three things — `background`,
285
- `primary`, `secondary`. shadcn's `@theme inline` is the later of the two, so on
286
- those three it wins, and the utility goes through the mapping above:
284
+ `primary`, `secondary`. The plugin's colors are theme defaults, so on those
285
+ three shadcn's `@theme inline` wins, and the utility goes through the mapping
286
+ above:
287
287
 
288
288
  ```
289
289
  bg-secondary → --color-secondary → var(--secondary) → var(--md-sys-color-secondary-container)
@@ -506,18 +506,17 @@ $ pnpm run lgtm
506
506
  ## CONTRIBUTING
507
507
 
508
508
  ```bash
509
- pnpm run storybook # the day-to-day loop -- no build needed, the stylesheets regenerate as you edit
510
- pnpm run build # dist/, plus the generated stylesheets -- both gitignored
509
+ pnpm run storybook # the day-to-day loop -- no build needed, `shadcn.css` regenerates as you edit
510
+ pnpm run build # dist/, plus the generated files -- all gitignored
511
511
  pnpm run lgtm # everything CI checks
512
512
  ```
513
513
 
514
- `tailwind.css`, `shadcn.css` and `registry-item.json` are generated — from
515
- `toTailwind()`, `toShadcnAliases()` and `toShadcnRegistryItem()` — and
516
- gitignored. `pnpm run build` writes them (`scripts/generate.mjs`); the two
517
- stylesheets also get a `src/` copy, which is what Storybook `@import`s, and in
518
- Storybook a Vite plugin (`.storybook/main.ts`) rewrites those at server start
519
- and again on every edit under `src/lib/`, so the stories never show a stale
520
- vocabulary.
514
+ `shadcn.css` and `registry-item.json` are generated — from `toShadcnAliases()`
515
+ and `toShadcnRegistryItem()` — and gitignored. `pnpm run build` writes them
516
+ (`scripts/generate.mjs`); `shadcn.css` also gets a `src/` copy, which is what
517
+ Storybook `@import`s, and in Storybook a Vite plugin (`.storybook/main.ts`)
518
+ rewrites it at server start and again on every edit under `src/lib/`, so the
519
+ stories never show a stale vocabulary.
521
520
 
522
521
  `generate.mjs` builds the registry item without `{ fallback: true }`, which is
523
522
  what keeps every one of those outputs a function of the _mapping_ rather than of
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 };