material-theme-builder 3.2.0 → 3.3.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 +284 -236
- package/dist/cli.js +520 -105
- package/dist/index.d.ts +30 -1
- package/dist/index.js +120 -67
- package/dist/react.d.ts +69 -49
- package/dist/react.js +120 -67
- package/dist/registry-item.json +75 -0
- package/dist/shadcn.css +43 -0
- package/dist/tailwind-plugin.d.ts +73 -0
- package/dist/tailwind-plugin.js +135 -0
- package/dist/tailwind.css +47 -81
- package/package.json +31 -10
- package/src/tailwind.css +0 -179
package/README.md
CHANGED
|
@@ -48,6 +48,8 @@ theme.toCss();
|
|
|
48
48
|
theme.toTailwind();
|
|
49
49
|
theme.toFlutter();
|
|
50
50
|
theme.toShadcn();
|
|
51
|
+
theme.toShadcnAliases();
|
|
52
|
+
theme.toShadcnRegistryItem({ fallback: true });
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
## CLI
|
|
@@ -127,247 +129,94 @@ return (
|
|
|
127
129
|
|
|
128
130
|
## Tailwind
|
|
129
131
|
|
|
130
|
-
Compatible through [theme variables](https://tailwindcss.com/docs/theme)
|
|
132
|
+
Compatible through [theme variables](https://tailwindcss.com/docs/theme), in two
|
|
133
|
+
halves — a stylesheet for the standard tokens, and a plugin for the custom
|
|
134
|
+
colors, the one part a shipped file cannot know:
|
|
131
135
|
|
|
132
136
|
```css
|
|
133
|
-
@
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
--color-surface-bright: var(--md-sys-color-surface-bright);
|
|
139
|
-
--color-surface-container-lowest: var(
|
|
140
|
-
--md-sys-color-surface-container-lowest
|
|
141
|
-
);
|
|
142
|
-
--color-surface-container-low: var(--md-sys-color-surface-container-low);
|
|
143
|
-
--color-surface-container: var(--md-sys-color-surface-container);
|
|
144
|
-
--color-surface-container-high: var(--md-sys-color-surface-container-high);
|
|
145
|
-
--color-surface-container-highest: var(
|
|
146
|
-
--md-sys-color-surface-container-highest
|
|
147
|
-
);
|
|
148
|
-
--color-on-surface: var(--md-sys-color-on-surface);
|
|
149
|
-
--color-on-surface-variant: var(--md-sys-color-on-surface-variant);
|
|
150
|
-
--color-outline: var(--md-sys-color-outline);
|
|
151
|
-
--color-outline-variant: var(--md-sys-color-outline-variant);
|
|
152
|
-
--color-inverse-surface: var(--md-sys-color-inverse-surface);
|
|
153
|
-
--color-inverse-on-surface: var(--md-sys-color-inverse-on-surface);
|
|
154
|
-
--color-primary: var(--md-sys-color-primary);
|
|
155
|
-
--color-on-primary: var(--md-sys-color-on-primary);
|
|
156
|
-
--color-primary-container: var(--md-sys-color-primary-container);
|
|
157
|
-
--color-on-primary-container: var(--md-sys-color-on-primary-container);
|
|
158
|
-
--color-primary-fixed: var(--md-sys-color-primary-fixed);
|
|
159
|
-
--color-primary-fixed-dim: var(--md-sys-color-primary-fixed-dim);
|
|
160
|
-
--color-on-primary-fixed: var(--md-sys-color-on-primary-fixed);
|
|
161
|
-
--color-on-primary-fixed-variant: var(
|
|
162
|
-
--md-sys-color-on-primary-fixed-variant
|
|
163
|
-
);
|
|
164
|
-
--color-inverse-primary: var(--md-sys-color-inverse-primary);
|
|
165
|
-
--color-secondary: var(--md-sys-color-secondary);
|
|
166
|
-
--color-on-secondary: var(--md-sys-color-on-secondary);
|
|
167
|
-
--color-secondary-container: var(--md-sys-color-secondary-container);
|
|
168
|
-
--color-on-secondary-container: var(--md-sys-color-on-secondary-container);
|
|
169
|
-
--color-secondary-fixed: var(--md-sys-color-secondary-fixed);
|
|
170
|
-
--color-secondary-fixed-dim: var(--md-sys-color-secondary-fixed-dim);
|
|
171
|
-
--color-on-secondary-fixed: var(--md-sys-color-on-secondary-fixed);
|
|
172
|
-
--color-on-secondary-fixed-variant: var(
|
|
173
|
-
--md-sys-color-on-secondary-fixed-variant
|
|
174
|
-
);
|
|
175
|
-
--color-tertiary: var(--md-sys-color-tertiary);
|
|
176
|
-
--color-on-tertiary: var(--md-sys-color-on-tertiary);
|
|
177
|
-
--color-tertiary-container: var(--md-sys-color-tertiary-container);
|
|
178
|
-
--color-on-tertiary-container: var(--md-sys-color-on-tertiary-container);
|
|
179
|
-
--color-tertiary-fixed: var(--md-sys-color-tertiary-fixed);
|
|
180
|
-
--color-tertiary-fixed-dim: var(--md-sys-color-tertiary-fixed-dim);
|
|
181
|
-
--color-on-tertiary-fixed: var(--md-sys-color-on-tertiary-fixed);
|
|
182
|
-
--color-on-tertiary-fixed-variant: var(
|
|
183
|
-
--md-sys-color-on-tertiary-fixed-variant
|
|
184
|
-
);
|
|
185
|
-
--color-error: var(--md-sys-color-error);
|
|
186
|
-
--color-on-error: var(--md-sys-color-on-error);
|
|
187
|
-
--color-error-container: var(--md-sys-color-error-container);
|
|
188
|
-
--color-on-error-container: var(--md-sys-color-on-error-container);
|
|
189
|
-
--color-scrim: var(--md-sys-color-scrim);
|
|
190
|
-
--color-shadow: var(--md-sys-color-shadow);
|
|
191
|
-
|
|
192
|
-
/* Shades */
|
|
193
|
-
|
|
194
|
-
--color-primary-50: var(--md-ref-palette-primary-95);
|
|
195
|
-
--color-primary-100: var(--md-ref-palette-primary-90);
|
|
196
|
-
--color-primary-200: var(--md-ref-palette-primary-80);
|
|
197
|
-
--color-primary-300: var(--md-ref-palette-primary-70);
|
|
198
|
-
--color-primary-400: var(--md-ref-palette-primary-60);
|
|
199
|
-
--color-primary-500: var(--md-ref-palette-primary-50);
|
|
200
|
-
--color-primary-600: var(--md-ref-palette-primary-40);
|
|
201
|
-
--color-primary-700: var(--md-ref-palette-primary-30);
|
|
202
|
-
--color-primary-800: var(--md-ref-palette-primary-20);
|
|
203
|
-
--color-primary-900: var(--md-ref-palette-primary-10);
|
|
204
|
-
--color-primary-950: var(--md-ref-palette-primary-5);
|
|
205
|
-
|
|
206
|
-
--color-secondary-50: var(--md-ref-palette-secondary-95);
|
|
207
|
-
--color-secondary-100: var(--md-ref-palette-secondary-90);
|
|
208
|
-
--color-secondary-200: var(--md-ref-palette-secondary-80);
|
|
209
|
-
--color-secondary-300: var(--md-ref-palette-secondary-70);
|
|
210
|
-
--color-secondary-400: var(--md-ref-palette-secondary-60);
|
|
211
|
-
--color-secondary-500: var(--md-ref-palette-secondary-50);
|
|
212
|
-
--color-secondary-600: var(--md-ref-palette-secondary-40);
|
|
213
|
-
--color-secondary-700: var(--md-ref-palette-secondary-30);
|
|
214
|
-
--color-secondary-800: var(--md-ref-palette-secondary-20);
|
|
215
|
-
--color-secondary-900: var(--md-ref-palette-secondary-10);
|
|
216
|
-
--color-secondary-950: var(--md-ref-palette-secondary-5);
|
|
217
|
-
|
|
218
|
-
--color-tertiary-50: var(--md-ref-palette-tertiary-95);
|
|
219
|
-
--color-tertiary-100: var(--md-ref-palette-tertiary-90);
|
|
220
|
-
--color-tertiary-200: var(--md-ref-palette-tertiary-80);
|
|
221
|
-
--color-tertiary-300: var(--md-ref-palette-tertiary-70);
|
|
222
|
-
--color-tertiary-400: var(--md-ref-palette-tertiary-60);
|
|
223
|
-
--color-tertiary-500: var(--md-ref-palette-tertiary-50);
|
|
224
|
-
--color-tertiary-600: var(--md-ref-palette-tertiary-40);
|
|
225
|
-
--color-tertiary-700: var(--md-ref-palette-tertiary-30);
|
|
226
|
-
--color-tertiary-800: var(--md-ref-palette-tertiary-20);
|
|
227
|
-
--color-tertiary-900: var(--md-ref-palette-tertiary-10);
|
|
228
|
-
--color-tertiary-950: var(--md-ref-palette-tertiary-5);
|
|
229
|
-
|
|
230
|
-
--color-error-50: var(--md-ref-palette-error-95);
|
|
231
|
-
--color-error-100: var(--md-ref-palette-error-90);
|
|
232
|
-
--color-error-200: var(--md-ref-palette-error-80);
|
|
233
|
-
--color-error-300: var(--md-ref-palette-error-70);
|
|
234
|
-
--color-error-400: var(--md-ref-palette-error-60);
|
|
235
|
-
--color-error-500: var(--md-ref-palette-error-50);
|
|
236
|
-
--color-error-600: var(--md-ref-palette-error-40);
|
|
237
|
-
--color-error-700: var(--md-ref-palette-error-30);
|
|
238
|
-
--color-error-800: var(--md-ref-palette-error-20);
|
|
239
|
-
--color-error-900: var(--md-ref-palette-error-10);
|
|
240
|
-
--color-error-950: var(--md-ref-palette-error-5);
|
|
241
|
-
|
|
242
|
-
--color-neutral-50: var(--md-ref-palette-neutral-95);
|
|
243
|
-
--color-neutral-100: var(--md-ref-palette-neutral-90);
|
|
244
|
-
--color-neutral-200: var(--md-ref-palette-neutral-80);
|
|
245
|
-
--color-neutral-300: var(--md-ref-palette-neutral-70);
|
|
246
|
-
--color-neutral-400: var(--md-ref-palette-neutral-60);
|
|
247
|
-
--color-neutral-500: var(--md-ref-palette-neutral-50);
|
|
248
|
-
--color-neutral-600: var(--md-ref-palette-neutral-40);
|
|
249
|
-
--color-neutral-700: var(--md-ref-palette-neutral-30);
|
|
250
|
-
--color-neutral-800: var(--md-ref-palette-neutral-20);
|
|
251
|
-
--color-neutral-900: var(--md-ref-palette-neutral-10);
|
|
252
|
-
--color-neutral-950: var(--md-ref-palette-neutral-5);
|
|
253
|
-
|
|
254
|
-
--color-neutral-variant-50: var(--md-ref-palette-neutral-variant-95);
|
|
255
|
-
--color-neutral-variant-100: var(--md-ref-palette-neutral-variant-90);
|
|
256
|
-
--color-neutral-variant-200: var(--md-ref-palette-neutral-variant-80);
|
|
257
|
-
--color-neutral-variant-300: var(--md-ref-palette-neutral-variant-70);
|
|
258
|
-
--color-neutral-variant-400: var(--md-ref-palette-neutral-variant-60);
|
|
259
|
-
--color-neutral-variant-500: var(--md-ref-palette-neutral-variant-50);
|
|
260
|
-
--color-neutral-variant-600: var(--md-ref-palette-neutral-variant-40);
|
|
261
|
-
--color-neutral-variant-700: var(--md-ref-palette-neutral-variant-30);
|
|
262
|
-
--color-neutral-variant-800: var(--md-ref-palette-neutral-variant-20);
|
|
263
|
-
--color-neutral-variant-900: var(--md-ref-palette-neutral-variant-10);
|
|
264
|
-
--color-neutral-variant-950: var(--md-ref-palette-neutral-variant-5);
|
|
265
|
-
|
|
266
|
-
/*
|
|
267
|
-
* Custom colors
|
|
268
|
-
*/
|
|
269
|
-
|
|
270
|
-
--color-myCustomColor1: var(--md-sys-color-my-custom-color-1);
|
|
271
|
-
--color-on-myCustomColor1: var(--md-sys-color-on-my-custom-color-1);
|
|
272
|
-
--color-myCustomColor1-container: var(
|
|
273
|
-
--md-sys-color-my-custom-color-1-container
|
|
274
|
-
);
|
|
275
|
-
--color-on-myCustomColor1-container: var(
|
|
276
|
-
--md-sys-color-on-my-custom-color-1-container
|
|
277
|
-
);
|
|
278
|
-
/* Shades */
|
|
279
|
-
--color-myCustomColor1-50: var(--md-ref-palette-my-custom-color-1-95);
|
|
280
|
-
--color-myCustomColor1-100: var(--md-ref-palette-my-custom-color-1-90);
|
|
281
|
-
--color-myCustomColor1-200: var(--md-ref-palette-my-custom-color-1-80);
|
|
282
|
-
--color-myCustomColor1-300: var(--md-ref-palette-my-custom-color-1-70);
|
|
283
|
-
--color-myCustomColor1-400: var(--md-ref-palette-my-custom-color-1-60);
|
|
284
|
-
--color-myCustomColor1-500: var(--md-ref-palette-my-custom-color-1-50);
|
|
285
|
-
--color-myCustomColor1-600: var(--md-ref-palette-my-custom-color-1-40);
|
|
286
|
-
--color-myCustomColor1-700: var(--md-ref-palette-my-custom-color-1-30);
|
|
287
|
-
--color-myCustomColor1-800: var(--md-ref-palette-my-custom-color-1-20);
|
|
288
|
-
--color-myCustomColor1-900: var(--md-ref-palette-my-custom-color-1-10);
|
|
289
|
-
--color-myCustomColor1-950: var(--md-ref-palette-my-custom-color-1-5);
|
|
290
|
-
|
|
291
|
-
--color-myCustomColor2: var(--md-sys-color-my-custom-color-2);
|
|
292
|
-
--color-on-myCustomColor2: var(--md-sys-color-on-my-custom-color-2);
|
|
293
|
-
--color-myCustomColor2-container: var(
|
|
294
|
-
--md-sys-color-my-custom-color-2-container
|
|
295
|
-
);
|
|
296
|
-
--color-on-myCustomColor2-container: var(
|
|
297
|
-
--md-sys-color-on-my-custom-color-2-container
|
|
298
|
-
);
|
|
299
|
-
/* Shades */
|
|
300
|
-
--color-myCustomColor2-50: var(--md-ref-palette-my-custom-color-2-95);
|
|
301
|
-
--color-myCustomColor2-100: var(--md-ref-palette-my-custom-color-2-90);
|
|
302
|
-
--color-myCustomColor2-200: var(--md-ref-palette-my-custom-color-2-80);
|
|
303
|
-
--color-myCustomColor2-300: var(--md-ref-palette-my-custom-color-2-70);
|
|
304
|
-
--color-myCustomColor2-400: var(--md-ref-palette-my-custom-color-2-60);
|
|
305
|
-
--color-myCustomColor2-500: var(--md-ref-palette-my-custom-color-2-50);
|
|
306
|
-
--color-myCustomColor2-600: var(--md-ref-palette-my-custom-color-2-40);
|
|
307
|
-
--color-myCustomColor2-700: var(--md-ref-palette-my-custom-color-2-30);
|
|
308
|
-
--color-myCustomColor2-800: var(--md-ref-palette-my-custom-color-2-20);
|
|
309
|
-
--color-myCustomColor2-900: var(--md-ref-palette-my-custom-color-2-10);
|
|
310
|
-
--color-myCustomColor2-950: var(--md-ref-palette-my-custom-color-2-5);
|
|
137
|
+
@import "tailwindcss";
|
|
138
|
+
|
|
139
|
+
@import "material-theme-builder/tailwind.css";
|
|
140
|
+
@plugin "material-theme-builder/tailwind" {
|
|
141
|
+
custom-colors: myCustomColor1, myCustomColor2;
|
|
311
142
|
}
|
|
312
143
|
```
|
|
313
144
|
|
|
314
|
-
|
|
145
|
+
No hand-written block either side. Each name listed brings its four scheme roles
|
|
146
|
+
and eleven shades — `bg-myCustomColor1`, `text-on-myCustomColor1`,
|
|
147
|
+
`bg-myCustomColor1-container`, `bg-myCustomColor1-300`. Drop the `@plugin` line
|
|
148
|
+
if you have no custom colors.
|
|
149
|
+
|
|
150
|
+
The plugin takes a `prefix` too, mirroring `builder({ prefix })`:
|
|
315
151
|
|
|
316
152
|
```css
|
|
317
|
-
@
|
|
153
|
+
@plugin "material-theme-builder/tailwind" {
|
|
154
|
+
prefix: my;
|
|
155
|
+
custom-colors: myCustomColor1;
|
|
156
|
+
}
|
|
318
157
|
```
|
|
319
158
|
|
|
320
|
-
|
|
159
|
+
The plugin can carry the standard tokens on its own — `@plugin` without the
|
|
160
|
+
`@import` — for a setup that would rather not import CSS at all. Read the
|
|
161
|
+
warning below first if you also use shadcn.
|
|
162
|
+
|
|
163
|
+
> [!TIP]
|
|
321
164
|
>
|
|
322
|
-
>
|
|
165
|
+
> Both halves declare their colors as
|
|
166
|
+
> [inlined theme values](https://tailwindcss.com/docs/theme#referencing-other-variables):
|
|
167
|
+
> `bg-primary` compiles to `background-color: var(--md-sys-color-primary)`,
|
|
168
|
+
> with no `--color-primary` in between. That matters for nesting — a
|
|
169
|
+
> `--color-primary` declared on `:root` would resolve against `:root`, out of
|
|
170
|
+
> reach of a nested `<Mtb>` re-declaring the M3 properties.
|
|
171
|
+
|
|
172
|
+
> [!WARNING]
|
|
323
173
|
>
|
|
324
|
-
>
|
|
325
|
-
>
|
|
326
|
-
>
|
|
327
|
-
> */
|
|
174
|
+
> Theme values a plugin contributes are _defaults_: an `@theme` block of your
|
|
175
|
+
> own wins over them whatever the order, where the stylesheet — being CSS —
|
|
176
|
+
> wins by import order.
|
|
328
177
|
>
|
|
329
|
-
>
|
|
330
|
-
>
|
|
331
|
-
>
|
|
332
|
-
>
|
|
333
|
-
>
|
|
334
|
-
> --color-on-myCustomColor1-container: var(
|
|
335
|
-
> --md-sys-color-on-my-custom-color-1-container
|
|
336
|
-
> );
|
|
337
|
-
> /* Shades */
|
|
338
|
-
> --color-myCustomColor1-50: var(--md-ref-palette-my-custom-color-1-95);
|
|
339
|
-
> --color-myCustomColor1-100: var(--md-ref-palette-my-custom-color-1-90);
|
|
340
|
-
> --color-myCustomColor1-200: var(--md-ref-palette-my-custom-color-1-80);
|
|
341
|
-
> --color-myCustomColor1-300: var(--md-ref-palette-my-custom-color-1-70);
|
|
342
|
-
> --color-myCustomColor1-400: var(--md-ref-palette-my-custom-color-1-60);
|
|
343
|
-
> --color-myCustomColor1-500: var(--md-ref-palette-my-custom-color-1-50);
|
|
344
|
-
> --color-myCustomColor1-600: var(--md-ref-palette-my-custom-color-1-40);
|
|
345
|
-
> --color-myCustomColor1-700: var(--md-ref-palette-my-custom-color-1-30);
|
|
346
|
-
> --color-myCustomColor1-800: var(--md-ref-palette-my-custom-color-1-20);
|
|
347
|
-
> --color-myCustomColor1-900: var(--md-ref-palette-my-custom-color-1-10);
|
|
348
|
-
> --color-myCustomColor1-950: var(--md-ref-palette-my-custom-color-1-5);
|
|
178
|
+
> That is why the standard tokens are left to the stylesheet. shadcn's
|
|
179
|
+
> `@theme inline` claims three names M3 also uses — `background`, `primary`,
|
|
180
|
+
> `secondary` — and with the two halves above the stylesheet takes them back,
|
|
181
|
+
> so shadcn changes nothing. Only if you drop the `@import` and let the plugin
|
|
182
|
+
> carry the standard tokens do you have to hand those three back yourself:
|
|
349
183
|
>
|
|
350
|
-
>
|
|
351
|
-
>
|
|
352
|
-
>
|
|
353
|
-
> --md-sys-color-
|
|
354
|
-
> );
|
|
355
|
-
>
|
|
356
|
-
> --md-sys-color-on-my-custom-color-2-container
|
|
357
|
-
> );
|
|
358
|
-
> /* Shades */
|
|
359
|
-
> --color-myCustomColor2-50: var(--md-ref-palette-my-custom-color-2-95);
|
|
360
|
-
> --color-myCustomColor2-100: var(--md-ref-palette-my-custom-color-2-90);
|
|
361
|
-
> --color-myCustomColor2-200: var(--md-ref-palette-my-custom-color-2-80);
|
|
362
|
-
> --color-myCustomColor2-300: var(--md-ref-palette-my-custom-color-2-70);
|
|
363
|
-
> --color-myCustomColor2-400: var(--md-ref-palette-my-custom-color-2-60);
|
|
364
|
-
> --color-myCustomColor2-500: var(--md-ref-palette-my-custom-color-2-50);
|
|
365
|
-
> --color-myCustomColor2-600: var(--md-ref-palette-my-custom-color-2-40);
|
|
366
|
-
> --color-myCustomColor2-700: var(--md-ref-palette-my-custom-color-2-30);
|
|
367
|
-
> --color-myCustomColor2-800: var(--md-ref-palette-my-custom-color-2-20);
|
|
368
|
-
> --color-myCustomColor2-900: var(--md-ref-palette-my-custom-color-2-10);
|
|
369
|
-
> --color-myCustomColor2-950: var(--md-ref-palette-my-custom-color-2-5);
|
|
184
|
+
> ```css
|
|
185
|
+
> @theme inline {
|
|
186
|
+
> --color-background: var(--md-sys-color-background);
|
|
187
|
+
> --color-primary: var(--md-sys-color-primary);
|
|
188
|
+
> --color-secondary: var(--md-sys-color-secondary);
|
|
189
|
+
> }
|
|
370
190
|
> ```
|
|
191
|
+
>
|
|
192
|
+
> Left alone, `bg-secondary` resolves through shadcn's `--secondary`, which the
|
|
193
|
+
> [shadcn](#shadcn) section below remaps to `secondary-container` — a tone 90
|
|
194
|
+
> where you asked for a tone 40, under text still colored `on-secondary`.
|
|
195
|
+
|
|
196
|
+
<details>
|
|
197
|
+
<summary>The theme variables the stylesheet declares</summary>
|
|
198
|
+
|
|
199
|
+
Generated from [`toTailwind()`](#programmatic-api), so the two cannot drift:
|
|
200
|
+
|
|
201
|
+
```css
|
|
202
|
+
@theme inline {
|
|
203
|
+
--color-background: var(--md-sys-color-background);
|
|
204
|
+
--color-error: var(--md-sys-color-error);
|
|
205
|
+
--color-error-container: var(--md-sys-color-error-container);
|
|
206
|
+
--color-inverse-on-surface: var(--md-sys-color-inverse-on-surface);
|
|
207
|
+
--color-inverse-primary: var(--md-sys-color-inverse-primary);
|
|
208
|
+
--color-inverse-surface: var(--md-sys-color-inverse-surface);
|
|
209
|
+
--color-on-background: var(--md-sys-color-on-background);
|
|
210
|
+
--color-on-error: var(--md-sys-color-on-error);
|
|
211
|
+
/* ... */
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
115 names in all — every M3 scheme token, plus eleven Tailwind shades for each
|
|
216
|
+
of `primary`, `secondary`, `tertiary`, `error`, `neutral` and
|
|
217
|
+
`neutral-variant`.
|
|
218
|
+
|
|
219
|
+
</details>
|
|
371
220
|
|
|
372
221
|
## shadcn
|
|
373
222
|
|
|
@@ -376,17 +225,155 @@ Pre-requisites:
|
|
|
376
225
|
- You should use
|
|
377
226
|
[`tailwind.cssVariables`](https://ui.shadcn.com/docs/theming#css-variables)
|
|
378
227
|
|
|
379
|
-
|
|
380
|
-
|
|
228
|
+
One command, from inside your project:
|
|
229
|
+
|
|
230
|
+
```sh
|
|
231
|
+
$ npx material-theme-builder shadcn-apply "#6750A4"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Or from nothing at all — `shadcn-init` scaffolds a stock shadcn app
|
|
235
|
+
(`shadcn init --preset b0 --template vite`), themes it and starts it:
|
|
236
|
+
|
|
237
|
+
```sh
|
|
238
|
+
$ npx material-theme-builder shadcn-init "#6750A4"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The verbs are shadcn's own — there, `init` is the new project and `apply` the
|
|
242
|
+
existing one — and they are prefixed because shadcn is one integration here
|
|
243
|
+
among Figma, CSS, Tailwind and Flutter: a bare `init` would read as "initialize
|
|
244
|
+
material-theme-builder", and would leave no room for a `tailwind-init` later.
|
|
245
|
+
|
|
246
|
+
Anything after a `--` is forwarded verbatim to the shadcn command underneath —
|
|
247
|
+
`shadcn init` for `shadcn-init`, `shadcn add` for `shadcn-apply` — so
|
|
248
|
+
`shadcn-init "#6750A4" -- --template next -n my-app` scaffolds Next instead.
|
|
249
|
+
Options of ours go before the separator; one written after it is refused, rather
|
|
250
|
+
than forwarded into an error from shadcn about a flag it has never heard of.
|
|
251
|
+
`--print` writes the equivalent shell chain and runs nothing.
|
|
252
|
+
|
|
253
|
+
`--shadcn-cli <spec>` pins which shadcn runs — `--shadcn-cli shadcn@4.18.0`, a
|
|
254
|
+
tag, a fork, anything `npx` resolves — defaulting to `shadcn@latest`. (Not
|
|
255
|
+
`--shadcn`: the root command has used that name since 3.2.0 for something else
|
|
256
|
+
entirely, a boolean that appends the alias block to `--format tailwind`.) It
|
|
257
|
+
reaches for neighbouring versions rather than back in time, though: the defaults
|
|
258
|
+
these commands pass are shadcn 4.x vocabulary (`--preset b0` is a 4.x preset
|
|
259
|
+
code), so pinning far enough back also means passing that era's preset after the
|
|
260
|
+
`--`.
|
|
261
|
+
|
|
262
|
+
Both do the same two things by hand, if you would rather: generate a registry
|
|
263
|
+
item for your source color, and install it the way you install any shadcn theme.
|
|
264
|
+
|
|
265
|
+
```sh
|
|
266
|
+
$ npx material-theme-builder "#6750A4" --format registry-item > mtb.json
|
|
267
|
+
$ npx shadcn@latest add ./mtb.json && rm mtb.json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Either way, that rewrites the values inside your existing `:root` and `.dark`
|
|
271
|
+
blocks, in place, pointing
|
|
272
|
+
[shadcn's CSS variables](https://ui.shadcn.com/docs/theming#list-of-variables)
|
|
273
|
+
at the M3 custom properties `<Mtb>` emits — so every shadcn component follows
|
|
274
|
+
whichever theme is above it in the tree — and leaves that theme's own colors in
|
|
275
|
+
as the `var()` fallbacks:
|
|
381
276
|
|
|
382
277
|
```css
|
|
383
278
|
:root {
|
|
384
|
-
|
|
279
|
+
--card: var(--md-sys-color-surface-container-low, oklch(0.968 0.012 317.742));
|
|
385
280
|
}
|
|
281
|
+
|
|
386
282
|
.dark {
|
|
387
|
-
|
|
283
|
+
--card: var(--md-sys-color-surface-container-low, oklch(0.227 0.01 303.714));
|
|
388
284
|
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
So it works both ways round: live under an `<Mtb>`, and static — server-rendered,
|
|
288
|
+
zero client JS — anywhere there is none.
|
|
289
|
+
|
|
290
|
+
Every option lands in those fallbacks, `--scheme` and `--contrast` included, and
|
|
291
|
+
`shadcn-init` and `shadcn-apply` take them all — so the item they generate is the one the
|
|
292
|
+
by-hand route would have produced:
|
|
293
|
+
|
|
294
|
+
```sh
|
|
295
|
+
$ npx material-theme-builder shadcn-apply "#6750A4" --scheme vibrant --contrast 0.5
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`--no-fallback` leaves the fallbacks out, on all three. `--custom-colors` is the
|
|
299
|
+
one option the two subcommands do not take, and that is not an oversight:
|
|
300
|
+
shadcn's variable set is fixed, so no component reads a custom color and a
|
|
301
|
+
registry item cannot carry one.
|
|
302
|
+
|
|
303
|
+
> [!WARNING]
|
|
304
|
+
>
|
|
305
|
+
> `shadcn add` overwrites shadcn's own `oklch()` values rather than keeping them
|
|
306
|
+
> anywhere, so they are not a safety net. Where nothing declares the M3
|
|
307
|
+
> properties _and_ there are no fallbacks, every variable resolves to nothing and
|
|
308
|
+
> components render transparent — `git diff` your CSS, or re-run `shadcn init`,
|
|
309
|
+
> to get shadcn's defaults back.
|
|
310
|
+
|
|
311
|
+
> [!NOTE]
|
|
312
|
+
>
|
|
313
|
+
> shadcn's CLI also appends a self-referential `--card: var(--card);` per
|
|
314
|
+
> variable to your `@theme inline` block. It is noise, not a bug on your side:
|
|
315
|
+
> those land _above_ your `:root`, so the real values win. Delete them if they
|
|
316
|
+
> bother you.
|
|
317
|
+
|
|
318
|
+
For the opposite trade — concrete `oklch()` values and no `var()` at all, frozen
|
|
319
|
+
at build time — see [`toShadcn()`](#programmatic-api).
|
|
320
|
+
|
|
321
|
+
<details>
|
|
322
|
+
<summary>Install the mapping alone, without generating anything</summary>
|
|
323
|
+
|
|
324
|
+
The package publishes one too, so `shadcn add` has something to fetch without a
|
|
325
|
+
build step of yours:
|
|
326
|
+
|
|
327
|
+
```sh
|
|
328
|
+
$ npx shadcn@latest add https://unpkg.com/material-theme-builder/registry-item.json
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
It is the mapping and nothing else — 31 `var()` references, no colors. Which is
|
|
332
|
+
why it can be published at all: it is the same file whatever your source color,
|
|
333
|
+
scheme or contrast, because those arrive at runtime from `<Mtb>`. And that is
|
|
334
|
+
also its one requirement — mount an `<Mtb>`, or emit
|
|
335
|
+
[`toCss()`](#programmatic-api) server-side, or nothing resolves. Generate your
|
|
336
|
+
own, as above, to have colors to fall back on.
|
|
337
|
+
|
|
338
|
+
</details>
|
|
339
|
+
|
|
340
|
+
<details>
|
|
341
|
+
<summary>Rather import a stylesheet than let the CLI edit your file</summary>
|
|
342
|
+
|
|
343
|
+
A stylesheet is shipped too, for setups that would rather keep the mapping in
|
|
344
|
+
one line they can delete:
|
|
389
345
|
|
|
346
|
+
```css
|
|
347
|
+
@import "tailwindcss";
|
|
348
|
+
@import "./shadcn.css"; /* shadcn's own `:root` and `.dark` */
|
|
349
|
+
@import "material-theme-builder/shadcn.css"; /* ...then ours */
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
> [!IMPORTANT]
|
|
353
|
+
>
|
|
354
|
+
> It has to come AFTER shadcn's own `:root { ... }` and `.dark { ... }`, which
|
|
355
|
+
> it overrides — so **not** at the top with your other imports, which is where
|
|
356
|
+
> an `@import` normally goes and where this one silently loses:
|
|
357
|
+
>
|
|
358
|
+
> ```css
|
|
359
|
+
> /* ✗ `--card` falls back to shadcn's grey; nothing warns you */
|
|
360
|
+
> @import "tailwindcss";
|
|
361
|
+
> @import "material-theme-builder/shadcn.css";
|
|
362
|
+
> @import "./shadcn.css";
|
|
363
|
+
> ```
|
|
364
|
+
>
|
|
365
|
+
> The registry item above exists to make this impossible to get wrong.
|
|
366
|
+
|
|
367
|
+
</details>
|
|
368
|
+
|
|
369
|
+
<details>
|
|
370
|
+
<summary>The variables it remaps</summary>
|
|
371
|
+
|
|
372
|
+
Both halves are generated from [`toShadcnAliases()`](#programmatic-api) and
|
|
373
|
+
[`toShadcnRegistryItem()`](#programmatic-api), off one mapping, so neither can
|
|
374
|
+
drift from the other:
|
|
375
|
+
|
|
376
|
+
```css
|
|
390
377
|
:root,
|
|
391
378
|
.dark {
|
|
392
379
|
--background: var(--md-sys-color-surface);
|
|
@@ -423,6 +410,8 @@ Simply override/remap
|
|
|
423
410
|
}
|
|
424
411
|
```
|
|
425
412
|
|
|
413
|
+
</details>
|
|
414
|
+
|
|
426
415
|
<details>
|
|
427
416
|
<summary>mapping details</summary>
|
|
428
417
|
see:
|
|
@@ -431,11 +420,6 @@ Simply override/remap
|
|
|
431
420
|
- https://gemini.google.com/share/51e072b6f1d2
|
|
432
421
|
</details>
|
|
433
422
|
|
|
434
|
-
> [!IMPORTANT]
|
|
435
|
-
>
|
|
436
|
-
> Make sure `:root, .dark { ... }` comes AFTER `.root { ... } .dark { ... }` to
|
|
437
|
-
> take precedence.
|
|
438
|
-
|
|
439
423
|
# Dev
|
|
440
424
|
|
|
441
425
|
## INSTALL
|
|
@@ -475,6 +459,39 @@ $ pnpm run lgtm
|
|
|
475
459
|
|
|
476
460
|
## CONTRIBUTING
|
|
477
461
|
|
|
462
|
+
```bash
|
|
463
|
+
pnpm run storybook # the day-to-day loop -- no build needed, the stylesheets regenerate as you edit
|
|
464
|
+
pnpm run build # dist/, plus the generated stylesheets -- both gitignored
|
|
465
|
+
pnpm run lgtm # everything CI checks
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
`tailwind.css`, `shadcn.css` and `registry-item.json` are generated — from
|
|
469
|
+
`toTailwind()`, `toShadcnAliases()` and `toShadcnRegistryItem()` — and
|
|
470
|
+
gitignored. `pnpm run build` writes them (`scripts/generate.mjs`); the two
|
|
471
|
+
stylesheets also get a `src/` copy, which is what Storybook `@import`s, and in
|
|
472
|
+
Storybook a Vite plugin (`.storybook/main.ts`) rewrites those at server start
|
|
473
|
+
and again on every edit under `src/lib/`, so the stories never show a stale
|
|
474
|
+
vocabulary.
|
|
475
|
+
|
|
476
|
+
`generate.mjs` builds the registry item without `{ fallback: true }`, which is
|
|
477
|
+
what keeps every one of those outputs a function of the _mapping_ rather than of
|
|
478
|
+
a color: `SOURCE` there is arbitrary, and has to stay able to be. The fallback
|
|
479
|
+
variant belongs to whoever knows a real source color — the CLI's
|
|
480
|
+
`--format registry-item`.
|
|
481
|
+
|
|
482
|
+
`src/styles/shadcn.css` is the other half of that arrangement, and is _not_
|
|
483
|
+
generated from anything here: it is pristine `shadcn init --preset b0` output,
|
|
484
|
+
committed verbatim — regenerate it with the recipe in its own header. Same for
|
|
485
|
+
the components, via `pnpm dlx shadcn@latest add <item> --overwrite`. All of it
|
|
486
|
+
is exempt from Prettier and from the repo's own lint conventions, so that a
|
|
487
|
+
regeneration diffs to nothing; see `.prettierignore` and `SHADCN_FILES` in
|
|
488
|
+
`eslint.config.mjs` for which paths `components.json` makes shadcn's territory.
|
|
489
|
+
|
|
490
|
+
The `Shadcn/dashboard-01` story is what checks the shadcn mapping end to end: it
|
|
491
|
+
renders one of [shadcn's blocks](https://ui.shadcn.com/blocks), unmodified,
|
|
492
|
+
under `<Mtb>`. Every other story paints from the M3 vocabulary directly, so none
|
|
493
|
+
of them would notice `shadcn.css` pointing a variable at the wrong role.
|
|
494
|
+
|
|
478
495
|
When submitting a pull request, please include a changeset to document your
|
|
479
496
|
changes:
|
|
480
497
|
|
|
@@ -491,3 +508,34 @@ m3 references:
|
|
|
491
508
|
| builder | roles |
|
|
492
509
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
493
510
|
| [<img width="2836" height="2266" alt="CleanShot 2026-01-14 at 08 58 40@2x" src="https://github.com/user-attachments/assets/e4b47c00-716f-4b08-b393-de306d5ce302" />](https://material-foundation.github.io/material-theme-builder/) | [<img width="2836" height="2266" alt="CleanShot 2026-01-14 at 09 01 23@2x" src="https://github.com/user-attachments/assets/826e502d-e173-43c4-807a-53d0ba075a88" />](https://m3.material.io/styles/color/roles) |
|
|
511
|
+
|
|
512
|
+
The spec itself, deep-linked to the sections that matter. `m3.material.io` is a
|
|
513
|
+
client-rendered SPA, so `#:~:text=` fragments get stripped on load — only these
|
|
514
|
+
section anchors work:
|
|
515
|
+
|
|
516
|
+
- [Color roles](https://m3.material.io/styles/color/roles) — the inventory:
|
|
517
|
+
_"26 standard color roles organized into six groups"_, which is what
|
|
518
|
+
`tokenDescriptions` is checked against
|
|
519
|
+
- [Color roles § Surface](https://m3.material.io/styles/color/roles#89f972b1-e372-494c-aabc-69aea34ed591)
|
|
520
|
+
— _"three surface roles: Surface / On surface / On surface variant"_. No
|
|
521
|
+
`surface variant`: the ink outlived its own background, hence the asymmetry
|
|
522
|
+
- [Color roles § Add-on color roles](https://m3.material.io/styles/color/roles#a5f6ea3d-d457-4c5d-94f4-55f3cdf6470b)
|
|
523
|
+
— fixed accents and surface dim/bright are add-ons, and _"most products won't
|
|
524
|
+
need to use these"_
|
|
525
|
+
- [Color system § What's new](https://m3.material.io/styles/color/system/overview#ca18ba03-a1ec-4bbb-a531-ae5396d3ee4a)
|
|
526
|
+
— the changelog. Feb 2023 is when tone-based surfaces replaced the +1…+5
|
|
527
|
+
elevation model
|
|
528
|
+
|
|
529
|
+
The Material Design blog is where the reasoning behind the color system lives —
|
|
530
|
+
and where changes to it get announced before the spec pages catch up:
|
|
531
|
+
|
|
532
|
+
- [Tone-based Surfaces in Material 3](https://m3.material.io/blog/tone-based-surface-color-m3)
|
|
533
|
+
— the surface roles replacing elevation overlays. The only first-party text
|
|
534
|
+
stating that `Surface Variant` gives way to `Surface Container Highest`
|
|
535
|
+
- [The science of color & design](https://m3.material.io/blog/science-of-color-design)
|
|
536
|
+
— HCT, and why a tone means the same contrast across hues: the basis of the
|
|
537
|
+
tonal palettes
|
|
538
|
+
- [Designing Harmony into Dynamic Color](https://m3.material.io/blog/dynamic-color-harmony)
|
|
539
|
+
— what `customColors[].blend` actually does to a custom color
|
|
540
|
+
- [Introducing Material Theme Builder](https://m3.material.io/blog/material-theme-builder)
|
|
541
|
+
— the tool this package reimplements
|