feat!: move the React bindings to material-theme-builder/react - #161
Merged
Conversation
The root entry re-exported `Mcu`, `useMcu` and `ExportButton`, so a framework
that splits server and client graphs registered every export of the client
module through that re-export. `import { builder } from "material-theme-builder"`
in a server component therefore shipped the React surface and the color
utilities to the browser, for a component the page never rendered -- and no
amount of tree-shaking undoes a client-reference registration.
Root is now `builder` alone; the bindings live behind `/react`, which is
where the ecosystem puts framework bindings (`motion/react`,
`@floating-ui/react`). A subpath for the React-free half would have worked
too, but left the default import the expensive one -- you'd have to know
about client references to reach for the cheap path.
Measured on a Next `output: "export"` app calling only `builder(...).toCss()`,
same source both sides: client chunks drop from 198 715 to 166 679 bytes gzip
(9 chunks to 8), server-rendered CSS byte-identical at 19 398 chars.
The root also now exports the `McuConfig` type, previously unreachable from
any entry point.
BREAKING CHANGE: `Mcu`, `useMcu` and `ExportButton` move to
`material-theme-builder/react`. `builder`, the CLI and
`material-theme-builder/tailwind.css` are unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Supersedes #160, same problem, opposite shape.
#160 was right about the diagnosis and the measurement — I reproduced both. It added
material-theme-builder/builderas a React-free subpath. This does the inversion instead: the framework-agnostic core stays at the root, the React bindings move behind/react.Why this way round
A subpath for the React-free half fixes the bundle but leaves the default import the expensive one — you'd have to know that a framework registers client references through a re-export to think of reaching for it. Since that's now the second pass on the same invariant (#156, then #160), removing the required knowledge beats documenting the workaround.
It also stops the package name from stuttering:
material-theme-builder/builderon a package calledmaterial-theme-builderis a sign the root holds something other than what it says. And it's how the ecosystem splits this —motion/motion/react,@floating-ui/dom/@floating-ui/react.Measured
Next
output: "export"app, layout does nothing butbuilder(...).toCss()into a<style>,<Mcu>never imported. Identical app source on both sides; only the installed package shape differs (published 2.2.0 vs this branch, packed).tonalSpotpresent−32 036 bytes gzip, byte-identical output. Within 90 bytes of #160's figure, measured on a different app — the mechanism reproduces.
Worth noting the saving is larger than
dist/react.jsweighs standalone (26 184 gzip): dropping it also collapses a whole chunk. I'd flagged that arithmetic as suspicious when reviewing #160; it holds up.Build
dist/index.jsno longer references./react.jsat all, so the index build stops holding it external:attw🟢 on all four resolution modes for all three entries, 40 tests pass,pnpm run lgtmclean.Also
The root now exports the
McuConfigtype, which was previously unreachable from any entry point — it was declared inreact.d.tsbut never exported, and the root only re-exportedbuilder.Left alone
dist/react.jsstill carries its own copy of the builder and@material/material-color-utilities(argbFromHex×12,TonalPalette×10 in both bundles). Pre-existing, unchanged by this PR, and only costs anything to a client component importingbuilderfrom the root and rendering<Mcu>. Deduping needs the React sources to import through the root entry, which is its own change.🤖 Generated with Claude Code