feat: Tailwind v4 plugin, and the shadcn remap as a stylesheet - #177
feat: Tailwind v4 plugin, and the shadcn remap as a stylesheet#177abernier wants to merge 3 commits into
Conversation
…heet
`material-theme-builder/tailwind` registers the whole M3 color system as
Tailwind colors, in place of the `@theme inline` block the README asked
everyone to paste:
@import "tailwindcss";
@plugin "material-theme-builder/tailwind" {
custom-colors: myCustomColor1, myCustomColor2;
}
Custom colors are the part a stylesheet could never cover -- their names live
in the app, not in the package -- so they were the manual step the README kept
warning about. The plugin takes them as an option and registers their four M3
roles and their shades.
The shadcn mapping moves the same way: `material-theme-builder/shadcn.css` is
one import instead of a 31-line block to copy. It stays a stylesheet rather
than an option on the plugin because it has to win over shadcn's own unlayered
`:root`/`.dark`, and a plugin can only reach `@layer base`.
Token names move to `src/lib/tokens.ts` so the plugin -- which runs inside the
CSS build -- reads the names without pulling Material Color Utilities in.
`tailwind.css` keeps working, tested against the plugin so the two cannot
drift, and gains the two roles it was missing: `--color-surface-tint` and
`--color-surface-variant`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Second commit, after review: the one-liner was hiding what the wall of declarations made obvious. The listings are back in the README — folded, next to the two rules that generate them:
Same for shadcn: the 31-line block is in the README again, plus a paragraph on how to read it (containers back shadcn's tinted fills, Three tests hold the README against the plugin — a listing that drifts fails the build instead of quietly misleading its reader. Checked by deleting a line from each: both fail as expected. |
7b03d9b to
aaabb50
Compare
|
Correction on the previous comment: the README tests are gone, and so is the generator I floated. The doc commit is now README-only — the listings are back, kept by hand. What still guards the mappings are the two tests on the files that actually ship: |
2097e64 to
7a40445
Compare
The one-line `@plugin` hid what used to be readable at a glance: which M3 role backs which utility, and which tone backs which shade. So the listings come back -- folded, next to the two rules that generate them, and with the shade → tone table that was only ever implicit in the wall of declarations. Same for shadcn: the 31-line block is in the README again, with a paragraph on how to read it (containers back shadcn's tinted fills, `outline-variant` its borders). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7a40445 to
3d82a89
Compare
`:root, .dark` would only have won by coming later in the sheet, and an
`@import` cannot promise that: Vite hoists it to the top of the file it appears
in, which would put the remap *before* the block it is meant to override --
silently, since a no-op mapping looks exactly like a working one until you
compare colors.
`:root:root, .dark.dark` selects the same elements one specificity step up.
Unlayered on both sides, so specificity decides, and it travels with the rule
wherever a bundler moves it. Verified in a browser: a `@layer base` rule loses
to an unlayered `:root` even with four times the specificity, which is also why
this cannot become an option on the plugin.
`toTailwind({ shadcn: true })` keeps the plain selectors -- that block gets
pasted, so its position, and what it outranks, is the reader's call.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6e6fa8b to
ed0958a
Compare
|
Reverted the breaking part: Also corrected a claim I made earlier in this PR and had wrong:
|
Supersedes #167 (Tailwind half only, closed) — this one also covers shadcn.
Tailwind
instead of the ~180-line
@theme inline { --color-*: var(--md-sys-color-*) }block.Custom colors are the part a stylesheet could never cover — their names live in the app, not in the package — so they were exactly the manual step the README kept warning about ("Do not forget to manually add your custom colors"). Each one registers its four M3 roles and its shades, under both spellings:
bg-myCustomColor1(what the README documented) andbg-my-custom-color-1(whattoTailwind()emits).Options:
prefix(defaultmd),custom-colors,shades(defaulttrue— off leaves Tailwind's ownneutral-*alone). Read case- and dash-insensitively, because Prettier lowercases CSS declaration names and would otherwise turn a user'scustomColors:intocustomcolors:on save. An unknown option throws rather than silently doing nothing.shadcn
instead of the 31-line
:root, .dark { … }block. The file already existed insrc/, it just wasn't exported.It stays a stylesheet rather than an option on the plugin, on purpose: a plugin can only reach
@layer base, and shadcn's own:root/.darkare unlayered — layered styles lose to unlayered ones whatever their specificity, so ashadcn: trueoption would have been a silent no-op on a default shadcnglobals.css. The "import it after" note stays, for that same cascade reason.Same reason behind a caveat now documented: for the color names shadcn and M3 share (
primary,secondary,background), shadcn's@theme inlinebeats the plugin's theme whatever the order — Tailwind gives a CSS@themeprecedence over a plugin's. Those utilities keep shadcn's semantics, whichshadcn.csshas already pointed at M3.Also
src/lib/tokens.ts, so the plugin — which runs inside the CSS build — reads them without pulling Material Color Utilities in (dist/tailwind.jsis 7 KB, no runtime dep). Re-exported frombuilder.ts; public surface unchanged.tailwind.csskeeps working and gains the two roles it was missing:--color-surface-tint,--color-surface-variant— found by a new test holding the shipped stylesheets against what the plugin registers.toTailwind(), name for name, so the two spellings of the theme can't drift.tailwindcssadded as an optional peer dep.Verified
pnpm run lgtmgreen (72 tests). Beyond unit tests, the builtdist/was run through a real@tailwindcss/postcssbuild: utilities, tonal shades, opacity modifiers (bg-primary/50), custom colors in both spellings, and a shadcn-shapedglobals.csswhere--backgroundends up resolving to--md-sys-color-surface.🤖 Generated with Claude Code