docs: correct the SSR story, trim the Tailwind and shadcn sections - #184
Merged
Conversation
abernier
force-pushed
the
docs/rsc-ssr-notes
branch
2 times, most recently
from
August 18, 2026 08:57
ab881d7 to
8485a31
Compare
The React section claimed `<Mtb>` "injects the CSS from the client", which
stopped being true once `MtbProvider` started rendering the `<style>` instead
of injecting it from an effect: the variables now ship in the server-rendered
HTML and are set on the first paint (locked by the `renderToStaticMarkup` test
in `src/Mtb.test.tsx`).
State that the component is isomorphic, say that a *server* layout is a fine
place for it (`{children}` is passed as a prop, so the pages it wraps stay
Server Components), and reframe the `builder`-in-an-RSC note as what it
actually is — the option for a non-interactive theme, with the layout example
it was missing. The kebab-case note moves below, so the two SSR paragraphs sit
next to each other.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
abernier
force-pushed
the
docs/rsc-ssr-notes
branch
from
August 18, 2026 08:58
8485a31 to
7ecdbf5
Compare
Same content, fewer words: short sentences instead of one carrying three clauses, a code block where prose was approximating one (the `--` passthrough now shows the command it was describing), and out with what the reader can infer — "no hand-written block either side", "for a setup that would rather not import CSS at all", "and that is not an oversight". Also says up front, right under the first snippet, that the `@import`/`@plugin` order does not matter — verified by compiling both orders through Tailwind's own `compile()`: identical output, and where the two halves do overlap the stylesheet wins either way, plugin theme values being defaults. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
<Mtb> and the RSC escape hatch…mponents.json` names The repo kept shadcn's own output in `src/styles/shadcn.css` and wrapped it in a `globals.css` that imported it, so the two halves could be stacked in the right order while every `@import` stayed at the top of a file. No scaffolded project looks like that: `shadcn init` writes one stylesheet, and the tokens sit inline in it. So there is one file now. `components.json` names it, the shadcn CLI owns everything above the marker comment, and mtb's two `@import`s and `@plugin` go below — which is exactly what the README tells a reader to do, dogfooded rather than approximated. Checked rather than assumed, both ways: - compiled through `@tailwindcss/postcss` (the pipeline `postcss.config.js` uses), the output is byte-identical before and after, with no warning. An `@import` after a rule is not where CSS puts one; Tailwind resolves it in place regardless, so Chromatic cannot see a difference. - on a throwaway scaffold, `shadcn-apply` rewrote 155 lines inside `:root` and `.dark` and left the trailing `@import` untouched. Drift is now fixed by re-running the CLI, where it used to mean diffing against a pristine copy. `should keep every @import ahead of the @plugin block` went with the old arrangement — it asserted an invariant this commit deliberately breaks. What replaces it checks the one that carries the setup: both generated halves are imported below everything shadcn owns, which is the only place they win. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
…ions The shadcn section opened on `shadcn-apply` and buried the stylesheet in a `<details>`. It now opens on the CSS to write, shown as the file a scaffold actually leaves — shadcn's `:root` and `.dark` inline, ours appended at the bottom — mirroring shadcn's own manual-install page so a reader recognises their file. `shadcn-apply` follows as `###`, for what it adds: colors to fall back on, and no import to position. Also, applied across both integration sections: - the Tailwind section no longer mentions shadcn. mtb is usable with Tailwind and no shadcn at all, and a caveat about their intersection belongs in the downstream one. - the plugin-only mode (`@plugin` without the `@import`) is no longer documented. It had no reason to be preferred over a one-line import, and it cost a paragraph plus a sixteen-line warning. The capability stays, still covered by `should compile to what tailwind.css compiles to`. - shorter sentences, fewer clauses, and out with what `--help` already says (`--shadcn-cli`) or a reader can infer. The `@import`'s position is the one thing the reader has to get right, so it is now tested: `should win the cascade only when imported after/before shadcn's blocks` compiles both placements against the scaffold's own `:root`/`.dark`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
abernier
added a commit
that referenced
this pull request
Aug 18, 2026
`postcss.config.js` makes Vite prepend `postcss-import`, which refuses an `@import` that does not lead the file — it warns and drops it. That is exactly the shape `globals.css` has since #184 inlined shadcn's scaffold above the two generated imports, so `shadcn.css` never reached the page: shadcn's own `:root` and `.dark` won, and the components stopped following the source color. Both the dev server and `vite build` were affected, so the Chromatic baseline is wrong too. Tailwind resolves those imports itself, wherever they sit — verified: the same file through `postcss([tailwindcss()])` alone keeps the mapping. So take PostCSS out of Vite's way in the two configs that build this repo's CSS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
abernier
added a commit
that referenced
this pull request
Aug 18, 2026
`--auto-accept-changes main` took whatever landed as the new baseline, which is how a broken shadcn mapping became the reference in #184 with no one looking. Changes get accepted in the PR review, where there is a diff to look at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
abernier
added a commit
that referenced
this pull request
Aug 18, 2026
`--auto-accept-changes main` took whatever landed as the new baseline, which is how a broken shadcn mapping became the reference in #184 with no one looking. Changes get accepted in the PR review, where there is a diff to look at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
abernier
added a commit
that referenced
this pull request
Aug 18, 2026
`postcss.config.js` makes Vite prepend `postcss-import`, which refuses an `@import` that does not lead the file — it warns and drops it. That is exactly the shape `globals.css` has since #184 inlined shadcn's scaffold above the two generated imports, so `shadcn.css` never reached the page: shadcn's own `:root` and `.dark` won, and the components stopped following the source color. Both the dev server and `vite build` were affected, so the Chromatic baseline is wrong too. Tailwind resolves those imports itself, wherever they sit — verified: the same file through `postcss([tailwindcss()])` alone keeps the mapping. So take PostCSS out of Vite's way in the two configs that build this repo's CSS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN
4 tasks
abernier
added a commit
that referenced
this pull request
Aug 18, 2026
`--auto-accept-changes main` took whatever landed as the new baseline, which is how a broken shadcn mapping became the reference in #184 with no one looking. Changes get accepted in the PR review, where there is a diff to look at. Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
abernier
added a commit
that referenced
this pull request
Aug 18, 2026
) * fix: win on specificity, so the imports can go where CSS puts them `toShadcnAliases()` emitted `:root, .dark`, which ties with shadcn's own blocks and so had to be imported below them — below other rules, which CSS forbids. A conforming parser drops such an `@import`, and one in the chain does: the `postcss-import` Vite prepends warns and skips it, taking the whole mapping with it. That is how the shadcn theming broke in #184, silently, in both the dev server and `vite build`. Doubling each selector, `:root:root`, outranks shadcn's blocks on specificity instead. Position stops mattering, so `globals.css` now imports both generated halves with the others, and `@plugin` moves up with them — its theme values are defaults, so its position never mattered either. On the three names both `@theme inline` blocks claim, shadcn's now wins, being the later one. Which is what the alias mapping is for: `bg-secondary` and `<Button variant="secondary">` land on the same color. Only `secondary` moves — `primary` maps to itself, and M3 `background` and `surface` are one color. The two rewritten tests were checked by mutation: both fail with the doubling undone, or with an import moved back below a rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: show the combined recipe where the two halves meet The shadcn section led with one import and never showed what the file looks like with the Tailwind half in it too -- which is what this repo's own globals.css does. It goes in the `<details>` that already covers the pairing, so the main line stays at one import. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: put the whole recipe in the shadcn snippet `shadcn.css` alone themes every component, which is why the section led with one import -- but nobody installs this package and then skips the M3 names. All three lines go in the snippet, each saying what it brings, and the prose says which one is load-bearing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: order the two imports as globals.css does Neither competes with the other -- one is a `@theme` block, the other a cascade block, no name in common -- so this is only about reading order. Generic first, shadcn-specific second, which is what the repo's own stylesheet already did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: name the tailwind.css line for what it gives you Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: reword the shadcn.css line Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: say plainly that only shadcn.css is required The other two halves are the Tailwind recipe, and what they buy is utility names for your own markup -- not the theming, which `shadcn.css` does alone. That was a sentence at the end of a paragraph; it is its own now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: cite MDN for the doubled selector It reads like a hack because it is one -- but a documented one, under "Increasing specificity by duplicating selector". MDN also says to comment every use, which is what both places now do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN * docs: fold the specificity note into the details that shows the block Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN --------- Co-authored-by: Claude Opus 5 (1M context) <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.
The React section of the README claimed
<Mtb>"injects the CSS from the client". That stopped being true onceMtbProviderstarted rendering its<style>instead of injecting it from an effect — the variables now ship in the server-rendered HTML and are set on the first paint, whichsrc/Mtb.test.tsxalready locks down with arenderToStaticMarkupassertion.Doc-only, no code change.
{children}in a layout" advice and adds that a server layout is fine:{children}is passed as a prop, so the pages it wraps stay Server Components. States that<Mtb>is isomorphic (server HTML → first paint, client takes over onsetMtbConfig).builder-in-an-RSC path as what it actually is: the option for a non-interactive theme (no client JS, nouseMtb), with theRootLayoutexample it was missing.pnpm lgtmgreen.🤖 Generated with Claude Code
https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN