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 +42 -43
- package/dist/cli.js +79 -112
- package/dist/index.d.ts +13 -3
- package/dist/index.js +77 -111
- package/dist/react.d.ts +6 -1
- package/dist/react.js +77 -111
- package/dist/tailwind-plugin.d.ts +13 -13
- package/package.json +1 -2
- package/dist/tailwind.css +0 -145
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[](https://www.chromatic.com/library?appId=695eb517cb602e59b4cc045c&branch=main)
|
|
3
3
|
[](https://main--695eb517cb602e59b4cc045c.chromatic.com)
|
|
4
4
|
|
|
5
|
-
Outputs [m3 colors](https://m3.material.io/styles/color/system/overview) `--md-sys-color
|
|
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
|
|
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) —
|
|
157
|
-
|
|
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
|
|
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
|
|
197
|
-
|
|
198
|
-
Generated from [`toTailwind()`](#programmatic-api), so the two cannot drift:
|
|
207
|
+
<summary>The names it declares</summary>
|
|
199
208
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
215
|
-
|
|
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" { /*
|
|
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
|
|
269
|
-
and what
|
|
270
|
-
`text-on-primary`, your custom colors. Drop
|
|
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`.
|
|
286
|
-
|
|
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,
|
|
510
|
-
pnpm run build # dist/, plus the generated
|
|
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
|
-
`
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
|
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
|
-
|
|
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 [
|
|
51
|
-
const
|
|
52
|
-
|
|
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,
|
|
82
|
-
return
|
|
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(
|
|
89
|
-
([
|
|
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
|
|
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(
|
|
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
|
|
500
|
+
jsonSchemes[name] = extractSchemeColors(composedScheme);
|
|
547
501
|
}
|
|
548
502
|
return jsonSchemes;
|
|
549
503
|
}
|
|
550
|
-
function
|
|
504
|
+
function refPalettesToJson() {
|
|
551
505
|
const jsonPalettes = {};
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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] =
|
|
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
|
|
538
|
+
harmonized: c.blend
|
|
593
539
|
}));
|
|
594
540
|
return {
|
|
595
541
|
seed,
|
|
596
542
|
coreColors,
|
|
597
543
|
extendedColors,
|
|
598
544
|
schemes: buildJsonSchemes(),
|
|
599
|
-
palettes:
|
|
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
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
|
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:
|
|
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,
|
|
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
|
-
|
|
1150
|
-
colorPalettes
|
|
1115
|
+
customColorRoles
|
|
1151
1116
|
);
|
|
1152
1117
|
const mergedColorsDark = mergeBaseAndCustomColors(
|
|
1153
1118
|
darkScheme,
|
|
1154
|
-
|
|
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
|
-
|
|
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:
|
|
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 };
|