material-theme-builder 3.2.0 → 4.0.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 +346 -252
- package/dist/cli.js +314 -117
- package/dist/index.d.ts +30 -1
- package/dist/index.js +135 -75
- package/dist/react.d.ts +69 -49
- package/dist/react.js +142 -90
- 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 +34 -11
- 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
|
|
@@ -95,19 +97,43 @@ import { Mtb } from "material-theme-builder/react";
|
|
|
95
97
|
>
|
|
96
98
|
> Typically wrapping `{children}` in a
|
|
97
99
|
> [layout](https://nextjs.org/docs/app/getting-started/layouts-and-pages#creating-a-layout).
|
|
100
|
+
>
|
|
101
|
+
> `<Mtb>` renders its `<style>`, so it works both server- and client-side.
|
|
102
|
+
> Client-side is what you want when the theme has to be interactive through
|
|
103
|
+
> `setMtbConfig`.
|
|
98
104
|
|
|
99
105
|
> [!NOTE]
|
|
100
106
|
>
|
|
101
|
-
>
|
|
102
|
-
>
|
|
107
|
+
> For a theme that is not interactive / never changes at runtime, skip the
|
|
108
|
+
> component entirely: the root entry holds `builder` alone, so a Server
|
|
109
|
+
> Component can call it and emit `toCss()` into the document itself — no client
|
|
110
|
+
> JS, and no `useMtb`.
|
|
111
|
+
>
|
|
112
|
+
> ```tsx
|
|
113
|
+
> import { builder } from "material-theme-builder";
|
|
114
|
+
>
|
|
115
|
+
> const css = builder("#0e1216", { scheme: "vibrant" }).toCss();
|
|
116
|
+
>
|
|
117
|
+
> export default function RootLayout({
|
|
118
|
+
> children,
|
|
119
|
+
> }: {
|
|
120
|
+
> children: React.ReactNode;
|
|
121
|
+
> }) {
|
|
122
|
+
> return (
|
|
123
|
+
> <html lang="en">
|
|
124
|
+
> <head>
|
|
125
|
+
> <style dangerouslySetInnerHTML={{ __html: css }} />
|
|
126
|
+
> </head>
|
|
127
|
+
> <body>{children}</body>
|
|
128
|
+
> </html>
|
|
129
|
+
> );
|
|
130
|
+
> }
|
|
131
|
+
> ```
|
|
103
132
|
|
|
104
133
|
> [!NOTE]
|
|
105
134
|
>
|
|
106
|
-
>
|
|
107
|
-
> `
|
|
108
|
-
> [React Server Component](https://react.dev/reference/rsc/server-components)
|
|
109
|
-
> you can call it and emit `toCss()` into the document yourself, without
|
|
110
|
-
> shipping components the page never renders.
|
|
135
|
+
> CSS varnames are always kebab-cased, e.g. `myCustomColor1` →
|
|
136
|
+
> `--md-sys-color-my-custom-color-1` / `--md-ref-palette-my-custom-color-1-<tone>`
|
|
111
137
|
|
|
112
138
|
## `useMtb`
|
|
113
139
|
|
|
@@ -127,247 +153,69 @@ return (
|
|
|
127
153
|
|
|
128
154
|
## Tailwind
|
|
129
155
|
|
|
130
|
-
Compatible through [theme variables](https://tailwindcss.com/docs/theme)
|
|
156
|
+
Compatible through [theme variables](https://tailwindcss.com/docs/theme) — a
|
|
157
|
+
stylesheet for the standard tokens, and a plugin for the custom colors:
|
|
158
|
+
|
|
159
|
+
```css
|
|
160
|
+
@import "tailwindcss";
|
|
161
|
+
|
|
162
|
+
@import "material-theme-builder/tailwind.css";
|
|
163
|
+
@plugin "material-theme-builder/tailwind" {
|
|
164
|
+
custom-colors: myCustomColor1, myCustomColor2;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Drop the `@plugin` line if you have no custom colors.
|
|
169
|
+
|
|
170
|
+
<details>
|
|
171
|
+
|
|
172
|
+
Each name listed brings its four scheme roles and eleven shades —
|
|
173
|
+
`bg-myCustomColor1`, `text-on-myCustomColor1`, `bg-myCustomColor1-container`,
|
|
174
|
+
`bg-myCustomColor1-300`.
|
|
175
|
+
|
|
176
|
+
`prefix` mirrors `builder({ prefix })`:
|
|
177
|
+
|
|
178
|
+
```css
|
|
179
|
+
@plugin "material-theme-builder/tailwind" {
|
|
180
|
+
prefix: my;
|
|
181
|
+
custom-colors: myCustomColor1;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
</details>
|
|
186
|
+
|
|
187
|
+
> [!TIP]
|
|
188
|
+
>
|
|
189
|
+
> Colors are declared as
|
|
190
|
+
> [inlined theme values](https://tailwindcss.com/docs/theme#referencing-other-variables):
|
|
191
|
+
> `bg-primary` compiles to `background-color: var(--md-sys-color-primary)`, with
|
|
192
|
+
> no `--color-primary` in between. That one would sit on `:root`, out of reach
|
|
193
|
+
> of a nested `<Mtb>`.
|
|
194
|
+
|
|
195
|
+
<details>
|
|
196
|
+
<summary>The theme variables the stylesheet declares</summary>
|
|
197
|
+
|
|
198
|
+
Generated from [`toTailwind()`](#programmatic-api), so the two cannot drift:
|
|
131
199
|
|
|
132
200
|
```css
|
|
133
201
|
@theme inline {
|
|
134
202
|
--color-background: var(--md-sys-color-background);
|
|
135
|
-
--color-
|
|
136
|
-
--color-
|
|
137
|
-
--color-surface-dim: var(--md-sys-color-surface-dim);
|
|
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);
|
|
203
|
+
--color-error: var(--md-sys-color-error);
|
|
204
|
+
--color-error-container: var(--md-sys-color-error-container);
|
|
153
205
|
--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
206
|
--color-inverse-primary: var(--md-sys-color-inverse-primary);
|
|
165
|
-
--color-
|
|
166
|
-
--color-on-
|
|
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);
|
|
207
|
+
--color-inverse-surface: var(--md-sys-color-inverse-surface);
|
|
208
|
+
--color-on-background: var(--md-sys-color-on-background);
|
|
186
209
|
--color-on-error: var(--md-sys-color-on-error);
|
|
187
|
-
|
|
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);
|
|
210
|
+
/* ... */
|
|
311
211
|
}
|
|
312
212
|
```
|
|
313
213
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
@import "material-theme-builder/tailwind.css";
|
|
318
|
-
```
|
|
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`.
|
|
319
217
|
|
|
320
|
-
>
|
|
321
|
-
>
|
|
322
|
-
> Do not forget to manually add your custom colors, as in:
|
|
323
|
-
>
|
|
324
|
-
> ```css
|
|
325
|
-
> /*
|
|
326
|
-
> * Custom colors
|
|
327
|
-
> */
|
|
328
|
-
>
|
|
329
|
-
> --color-myCustomColor1: var(--md-sys-color-my-custom-color-1);
|
|
330
|
-
> --color-on-myCustomColor1: var(--md-sys-color-on-my-custom-color-1);
|
|
331
|
-
> --color-myCustomColor1-container: var(
|
|
332
|
-
> --md-sys-color-my-custom-color-1-container
|
|
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);
|
|
349
|
-
>
|
|
350
|
-
> --color-myCustomColor2: var(--md-sys-color-my-custom-color-2);
|
|
351
|
-
> --color-on-myCustomColor2: var(--md-sys-color-on-my-custom-color-2);
|
|
352
|
-
> --color-myCustomColor2-container: var(
|
|
353
|
-
> --md-sys-color-my-custom-color-2-container
|
|
354
|
-
> );
|
|
355
|
-
> --color-on-myCustomColor2-container: var(
|
|
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);
|
|
370
|
-
> ```
|
|
218
|
+
</details>
|
|
371
219
|
|
|
372
220
|
## shadcn
|
|
373
221
|
|
|
@@ -376,19 +224,110 @@ Pre-requisites:
|
|
|
376
224
|
- You should use
|
|
377
225
|
[`tailwind.cssVariables`](https://ui.shadcn.com/docs/theming#css-variables)
|
|
378
226
|
|
|
379
|
-
|
|
380
|
-
[
|
|
227
|
+
In your
|
|
228
|
+
[`globals.css`](https://ui.shadcn.com/docs/installation/manual#configure-styles):
|
|
381
229
|
|
|
382
230
|
```css
|
|
231
|
+
@import "tailwindcss";
|
|
232
|
+
@import "tw-animate-css";
|
|
233
|
+
@import "shadcn/tailwind.css";
|
|
234
|
+
|
|
235
|
+
/* 👇🏻 ADD THIS 👇🏻 */
|
|
236
|
+
@import "material-theme-builder/tailwind.css"; /* the M3 tw classNames (optional) */
|
|
237
|
+
@import "material-theme-builder/shadcn.css"; /* shadcn's variables remapping on M3 */
|
|
238
|
+
@plugin "material-theme-builder/tailwind" { /* your custom colors (optional) */
|
|
239
|
+
custom-colors: myCustomColor1, myCustomColor2;
|
|
240
|
+
}
|
|
241
|
+
/* 👆🏻 ADD THIS 👆🏻 */
|
|
242
|
+
|
|
243
|
+
@custom-variant dark (&:is(.dark *));
|
|
244
|
+
|
|
245
|
+
@theme inline {
|
|
246
|
+
--color-background: var(--background);
|
|
247
|
+
...
|
|
248
|
+
}
|
|
249
|
+
|
|
383
250
|
:root {
|
|
384
|
-
|
|
251
|
+
--radius: 0.625rem;
|
|
252
|
+
--background: oklch(1 0 0);
|
|
253
|
+
...
|
|
385
254
|
}
|
|
255
|
+
|
|
386
256
|
.dark {
|
|
387
|
-
|
|
257
|
+
--background: oklch(0.145 0 0);
|
|
258
|
+
...
|
|
388
259
|
}
|
|
260
|
+
```
|
|
389
261
|
|
|
390
|
-
:
|
|
391
|
-
.
|
|
262
|
+
`shadcn.css` is the one that matters: it points
|
|
263
|
+
[shadcn's variables](https://ui.shadcn.com/docs/theming#list-of-variables) at
|
|
264
|
+
the M3 custom properties, so every shadcn component follows whichever `<Mtb>` is
|
|
265
|
+
above it in the tree. It carries no colors of its own — mount an `<Mtb>`, or
|
|
266
|
+
emit [`toCss()`](#programmatic-api) server-side, or nothing resolves.
|
|
267
|
+
|
|
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.
|
|
272
|
+
|
|
273
|
+
For the opposite trade — concrete `oklch()` values and no `var()` at all, frozen
|
|
274
|
+
at build time — see [`toShadcn()`](#programmatic-api).
|
|
275
|
+
|
|
276
|
+
<details>
|
|
277
|
+
<summary>The three names both halves claim</summary>
|
|
278
|
+
|
|
279
|
+
> [!NOTE]
|
|
280
|
+
>
|
|
281
|
+
> Written down for the record. It moves one utility by one role, and you almost
|
|
282
|
+
> certainly do not need to care.
|
|
283
|
+
|
|
284
|
+
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:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
bg-secondary → --color-secondary → var(--secondary) → var(--md-sys-color-secondary-container)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Without shadcn it is one hop shorter, and lands on the role of the same name:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
bg-secondary → --color-secondary → var(--md-sys-color-secondary)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Same destination either way, M3 — just not the same role. And only for
|
|
299
|
+
`secondary`: `primary` maps to `primary`, and M3 `background` and `surface` are
|
|
300
|
+
the same color.
|
|
301
|
+
|
|
302
|
+
If you ever want the M3 role itself, `<Mtb>` still emits it:
|
|
303
|
+
|
|
304
|
+
```html
|
|
305
|
+
<div class="bg-[var(--md-sys-color-secondary)]"></div>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
or give it a name of its own:
|
|
309
|
+
|
|
310
|
+
```css
|
|
311
|
+
@theme inline {
|
|
312
|
+
--color-m3-secondary: var(--md-sys-color-secondary);
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
</details>
|
|
317
|
+
|
|
318
|
+
<details>
|
|
319
|
+
<summary>The variables it remaps</summary>
|
|
320
|
+
|
|
321
|
+
Both halves are generated from [`toShadcnAliases()`](#programmatic-api) and
|
|
322
|
+
[`toShadcnRegistryItem()`](#programmatic-api), off one mapping, so they cannot
|
|
323
|
+
drift. The selectors are doubled so the block outranks shadcn's own `:root` and
|
|
324
|
+
`.dark` on
|
|
325
|
+
[specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity#increasing_specificity_by_duplicating_selector)
|
|
326
|
+
rather than on order — which is what lets the `@import` sit with your others.
|
|
327
|
+
|
|
328
|
+
```css
|
|
329
|
+
:root:root,
|
|
330
|
+
.dark.dark {
|
|
392
331
|
--background: var(--md-sys-color-surface);
|
|
393
332
|
--foreground: var(--md-sys-color-on-surface);
|
|
394
333
|
--card: var(--md-sys-color-surface-container-low);
|
|
@@ -423,18 +362,109 @@ Simply override/remap
|
|
|
423
362
|
}
|
|
424
363
|
```
|
|
425
364
|
|
|
426
|
-
<details>
|
|
427
|
-
<summary>mapping details</summary>
|
|
428
|
-
see:
|
|
429
|
-
|
|
430
|
-
- https://chatgpt.com/share/6899f20a-422c-8011-a072-62fb649589a0
|
|
431
|
-
- https://gemini.google.com/share/51e072b6f1d2
|
|
432
365
|
</details>
|
|
433
366
|
|
|
434
|
-
|
|
367
|
+
### `shadcn-apply`
|
|
368
|
+
|
|
369
|
+
The alternative, for colors to fall back on and no import to place. One command,
|
|
370
|
+
from inside your project:
|
|
371
|
+
|
|
372
|
+
```sh
|
|
373
|
+
$ npx material-theme-builder shadcn-apply "#6750A4"
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
From nothing at all, scaffold with shadcn's own CLI first — what this repo
|
|
377
|
+
dogfoods:
|
|
378
|
+
|
|
379
|
+
```sh
|
|
380
|
+
$ npx shadcn@latest init --preset b0 --name material-theme-app
|
|
381
|
+
$ cd material-theme-app && npx material-theme-builder shadcn-apply "#6750A4"
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
It generates a registry item for your source color and hands it to `shadcn add`,
|
|
385
|
+
which rewrites the values inside your existing `:root` and `.dark` blocks, in
|
|
386
|
+
place. Same mapping as the stylesheet, with that theme's own colors left in as
|
|
387
|
+
the `var()` fallbacks:
|
|
388
|
+
|
|
389
|
+
```css
|
|
390
|
+
:root {
|
|
391
|
+
--card: var(--md-sys-color-surface-container-low, oklch(0.968 0.012 317.742));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.dark {
|
|
395
|
+
--card: var(--md-sys-color-surface-container-low, oklch(0.227 0.01 303.714));
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
So it works with no `<Mtb>` at all — the fallbacks render the theme statically,
|
|
400
|
+
server-rendered, zero client JS. Your old values are overwritten, not kept
|
|
401
|
+
anywhere: `git diff` is the undo.
|
|
402
|
+
|
|
403
|
+
Both steps by hand, if you would rather:
|
|
404
|
+
|
|
405
|
+
```sh
|
|
406
|
+
$ npx material-theme-builder "#6750A4" --format registry-item > mtb.json
|
|
407
|
+
$ npx shadcn@latest add ./mtb.json && rm mtb.json
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
`shadcn-apply` takes every theme option `material-theme-builder` itself takes,
|
|
411
|
+
and they all land in those fallbacks:
|
|
412
|
+
|
|
413
|
+
```sh
|
|
414
|
+
$ npx material-theme-builder shadcn-apply "#6750A4" --scheme vibrant --contrast 0.5
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
<details>
|
|
418
|
+
<summary>The rest of the options</summary>
|
|
419
|
+
|
|
420
|
+
`--no-fallback` leaves the fallbacks out, on both — so shadcn's own colors are
|
|
421
|
+
dropped rather than kept in reserve. Nothing then declares those variables
|
|
422
|
+
except an `<Mtb>` or a [`toCss()`](#programmatic-api): without one, they resolve
|
|
423
|
+
to nothing and the components render transparent.
|
|
424
|
+
|
|
425
|
+
`--custom-colors` is the one option missing: shadcn's variable set is fixed, so
|
|
426
|
+
a registry item cannot carry one.
|
|
427
|
+
|
|
428
|
+
Anything after a `--` is forwarded verbatim to `shadcn add`. Our options go
|
|
429
|
+
before it:
|
|
430
|
+
|
|
431
|
+
```sh
|
|
432
|
+
$ npx material-theme-builder shadcn-apply "#6750A4" -- --overwrite --dry-run
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
> [!NOTE]
|
|
435
436
|
>
|
|
436
|
-
>
|
|
437
|
-
>
|
|
437
|
+
> shadcn's CLI also appends a self-referential `--card: var(--card);` per
|
|
438
|
+
> variable to your `@theme inline` block. Noise, not a bug: they land _above_
|
|
439
|
+
> your `:root`, so the real values win. Delete them if they bother you.
|
|
440
|
+
|
|
441
|
+
</details>
|
|
442
|
+
|
|
443
|
+
<details>
|
|
444
|
+
<summary>Install the mapping alone, without generating anything</summary>
|
|
445
|
+
|
|
446
|
+
The package publishes a registry item too, so `shadcn add` has something to
|
|
447
|
+
fetch without a build step:
|
|
448
|
+
|
|
449
|
+
```sh
|
|
450
|
+
$ npx shadcn@latest add https://unpkg.com/material-theme-builder/registry-item.json
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
It is the stylesheet's content, installed the registry way: the mapping and
|
|
454
|
+
nothing else, no colors to fall back on. Generate your own, as above, to have
|
|
455
|
+
some.
|
|
456
|
+
|
|
457
|
+
</details>
|
|
458
|
+
|
|
459
|
+
<details>
|
|
460
|
+
<summary>mapping details</summary>
|
|
461
|
+
|
|
462
|
+
see:
|
|
463
|
+
|
|
464
|
+
- https://chatgpt.com/share/6899f20a-422c-8011-a072-62fb649589a0
|
|
465
|
+
- https://gemini.google.com/share/51e072b6f1d2
|
|
466
|
+
|
|
467
|
+
</details>
|
|
438
468
|
|
|
439
469
|
# Dev
|
|
440
470
|
|
|
@@ -475,6 +505,39 @@ $ pnpm run lgtm
|
|
|
475
505
|
|
|
476
506
|
## CONTRIBUTING
|
|
477
507
|
|
|
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
|
|
511
|
+
pnpm run lgtm # everything CI checks
|
|
512
|
+
```
|
|
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.
|
|
521
|
+
|
|
522
|
+
`generate.mjs` builds the registry item without `{ fallback: true }`, which is
|
|
523
|
+
what keeps every one of those outputs a function of the _mapping_ rather than of
|
|
524
|
+
a color: `SOURCE` there is arbitrary, and has to stay able to be. The fallback
|
|
525
|
+
variant belongs to whoever knows a real source color — the CLI's
|
|
526
|
+
`--format registry-item`.
|
|
527
|
+
|
|
528
|
+
`src/styles/shadcn.css` is the other half of that arrangement, and is _not_
|
|
529
|
+
generated from anything here: it is pristine `shadcn init --preset b0` output,
|
|
530
|
+
committed verbatim — regenerate it with the recipe in its own header. Same for
|
|
531
|
+
the components, via `pnpm dlx shadcn@latest add <item> --overwrite`. All of it
|
|
532
|
+
is exempt from Prettier and from the repo's own lint conventions, so that a
|
|
533
|
+
regeneration diffs to nothing; see `.prettierignore` and `SHADCN_FILES` in
|
|
534
|
+
`eslint.config.mjs` for which paths `components.json` makes shadcn's territory.
|
|
535
|
+
|
|
536
|
+
The `Shadcn/dashboard-01` story is what checks the shadcn mapping end to end: it
|
|
537
|
+
renders one of [shadcn's blocks](https://ui.shadcn.com/blocks), unmodified,
|
|
538
|
+
under `<Mtb>`. Every other story paints from the M3 vocabulary directly, so none
|
|
539
|
+
of them would notice `shadcn.css` pointing a variable at the wrong role.
|
|
540
|
+
|
|
478
541
|
When submitting a pull request, please include a changeset to document your
|
|
479
542
|
changes:
|
|
480
543
|
|
|
@@ -491,3 +554,34 @@ m3 references:
|
|
|
491
554
|
| builder | roles |
|
|
492
555
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
493
556
|
| [<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) |
|
|
557
|
+
|
|
558
|
+
The spec itself, deep-linked to the sections that matter. `m3.material.io` is a
|
|
559
|
+
client-rendered SPA, so `#:~:text=` fragments get stripped on load — only these
|
|
560
|
+
section anchors work:
|
|
561
|
+
|
|
562
|
+
- [Color roles](https://m3.material.io/styles/color/roles) — the inventory:
|
|
563
|
+
_"26 standard color roles organized into six groups"_, which is what
|
|
564
|
+
`tokenDescriptions` is checked against
|
|
565
|
+
- [Color roles § Surface](https://m3.material.io/styles/color/roles#89f972b1-e372-494c-aabc-69aea34ed591)
|
|
566
|
+
— _"three surface roles: Surface / On surface / On surface variant"_. No
|
|
567
|
+
`surface variant`: the ink outlived its own background, hence the asymmetry
|
|
568
|
+
- [Color roles § Add-on color roles](https://m3.material.io/styles/color/roles#a5f6ea3d-d457-4c5d-94f4-55f3cdf6470b)
|
|
569
|
+
— fixed accents and surface dim/bright are add-ons, and _"most products won't
|
|
570
|
+
need to use these"_
|
|
571
|
+
- [Color system § What's new](https://m3.material.io/styles/color/system/overview#ca18ba03-a1ec-4bbb-a531-ae5396d3ee4a)
|
|
572
|
+
— the changelog. Feb 2023 is when tone-based surfaces replaced the +1…+5
|
|
573
|
+
elevation model
|
|
574
|
+
|
|
575
|
+
The Material Design blog is where the reasoning behind the color system lives —
|
|
576
|
+
and where changes to it get announced before the spec pages catch up:
|
|
577
|
+
|
|
578
|
+
- [Tone-based Surfaces in Material 3](https://m3.material.io/blog/tone-based-surface-color-m3)
|
|
579
|
+
— the surface roles replacing elevation overlays. The only first-party text
|
|
580
|
+
stating that `Surface Variant` gives way to `Surface Container Highest`
|
|
581
|
+
- [The science of color & design](https://m3.material.io/blog/science-of-color-design)
|
|
582
|
+
— HCT, and why a tone means the same contrast across hues: the basis of the
|
|
583
|
+
tonal palettes
|
|
584
|
+
- [Designing Harmony into Dynamic Color](https://m3.material.io/blog/dynamic-color-harmony)
|
|
585
|
+
— what `customColors[].blend` actually does to a custom color
|
|
586
|
+
- [Introducing Material Theme Builder](https://m3.material.io/blog/material-theme-builder)
|
|
587
|
+
— the tool this package reimplements
|