website: shadcn tokens on Material Design 3 colour roles - #143
Merged
abernier merged 4 commits intoJul 31, 2026
Merged
Conversation
Wires material-theme-builder into the root layout: `<Mcu>` generates the m3 palette from one source hex, and every shadcn token in globals.css now reads the matching `--md-sys-color-*` role. The stock oklch neutrals stay as each `var()` fallback -- `<Mcu>` injects its stylesheet on hydration, so those are what paints first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`<Mcu>` fills its <style> tag from useInsertionEffect, so on an `output: "export"` site the first paint lands before a single colour exists -- the prerendered HTML carried none of the `--md-sys-color-*` definitions, only `var()` references to them. `bin/gen-mcu-css.mjs` now writes the palette ahead of dev/build and globals.css imports it, so it compiles into the stylesheet and ships in the HTML. Colours are correct at first paint and identical after hydration; the client bundle goes back to its previous size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…er 2.2.0 2.2.0 renders `<Mcu>`'s `<style>` instead of injecting it on hydration, and keeps `"use client"` off `builder` so a server component can call it. The generator script existed only to work around the second half, so it goes: `layout.tsx` calls `builder(...).toCss()` at module scope and renders the result, React hoisting it into `<head>` via `href`/`precedence`. Same guarantee as before -- the palette is in the prerendered HTML, correct at first paint, untouched by hydration -- with one file less and no gitignored build artefact. One regression, temporary: importing `builder` from the package's main entry pulls its `"use client"` module into the client graph, +32 kB gzip for a `<Mcu>` nothing renders. A React-free subpath is queued upstream (abernier/material-theme-builder#160). `minimumReleaseAgeExclude` is pnpm's supply-chain gate letting a same-day 2.2.0 install; removable once the version has aged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3.0.0 moves the React bindings to `material-theme-builder/react` and leaves `builder` alone at the root. We only ever imported `builder`, so the migration is the version bump and nothing else. It does undo the 32 kB the previous commit noted: the root no longer re-exports a `"use client"` module, so Next stops registering `Mcu` and the colour utilities as client references. Client chunks are back to 313 214 bytes gzip -- the number before any of this landed -- with no `md-sys-color` string left in them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
abernier
force-pushed
the
claude/material-theme-builder-setup-1c2f32
branch
from
July 31, 2026 15:53
a06fa70 to
40b86de
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #142.
Puts the website's colour system on material-theme-builder. The three PRs below this one moved the nav onto shadcn components and shadcn tokens; the tokens themselves were still shadcn's stock neutral greys. They now come out of Material Design 3 — every one of them maps to an
--md-sys-color-*role, per the shadcn mapping in the README.One hex
Poimandres' signature mint. Surfaces, badges, the select popover, focus rings, the empty state — all of it falls out of those two lines in
app/layout.tsx. Change them and the site retunes;contrast, core-colour overrides andcustomColorsgo in the same call.Why the layout and not
<Mcu>layout.tsxis a server component, sobuilder(...).toCss()runs once at build time and the CSS ships inside the prerendered HTML — React hoists the<style>into<head>viahref/precedence.<Mcu>would put the same CSS in the page (it renders its<style>as of 2.2.0) but recomputes the palette on the client at hydration for a theme that never changes at runtime. This app isoutput: "export": the browser gets the whole page and paints it long before any JS runs, so anything that supplies the colours later gives you a frame with none of them at all.That was the bug in the first two commits of this PR, and it's fixed upstream now:
<Mcu>renders its<style>instead of injecting it fromuseInsertionEffect"use client"offbuilder, so a server component can call itmaterial-theme-builder/react, root left React-free. We only importbuilderfrom the root, so the migration was the version bump alone.Verified on the built artefact
out/served over http, both schemes, sampled atdomcontentloaded— before hydration:--md-sys-color-surfaceat first paint#f5fbf7#0e1513bodybackground at first paint<style>mutations caused by hydration--md-sys-color-surface:is present inout/index.html; the<style>sits in<head>, 19 398 chars, unescaped.Client bundle
Unchanged: 313 214 bytes gzip, the same number as #142 before any of this. No
md-sys-color,tonalSpotormcu-stylesstring survives in the chunks.That took a detour. On 2.2.0 the root entry re-exported the package's
"use client"module, and Next registers every export of one it reaches — soMcuand@material/material-color-utilitiesshipped to the browser (+32 kB gzip) even though nothing renders them. 3.0.0 puts the React bindings behind/reactand leaves the root React-free, which removes it at the source.Not in scope
The panels that were already hardcoded light stay hardcoded light: the nav's HIDE toggle (
Nav.tsx), andInfo/Devon the demo page. They're@scoped inline CSS, so they'd need the same treatment #140 gave the shell before they can follow the palette.Lockfile
material-theme-builder@3.0.0plus itszoddependency; earlier in the branchpnpm addalso deduped a few@babel/*transitives that had two copies pinned.minimumReleaseAgeExcludeinpnpm-workspace.yamlis pnpm 11's supply-chain gate — 3.0.0 was published the same day, and the entry can be dropped once it has aged.Checks
tsc --noEmit,next lintandnext build(163 pages) clean, console clean. Exercised in both schemes: the demo list, the library dropdown, a demo page, and the pre-hydration paint on the real static artefact.🤖 Generated with Claude Code