Skip to content

docs: correct the SSR story, trim the Tailwind and shadcn sections - #184

Merged
abernier merged 4 commits into
mainfrom
docs/rsc-ssr-notes
Aug 18, 2026
Merged

docs: correct the SSR story, trim the Tailwind and shadcn sections#184
abernier merged 4 commits into
mainfrom
docs/rsc-ssr-notes

Conversation

@abernier

Copy link
Copy Markdown
Owner

The React section of the README claimed <Mtb> "injects the CSS from the client". That stopped being true once MtbProvider started 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, which src/Mtb.test.tsx already locks down with a renderToStaticMarkup assertion.

Doc-only, no code change.

  • TIP — keeps the "wrap {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 on setMtbConfig).
  • NOTE — reframes the builder-in-an-RSC path as what it actually is: the option for a non-interactive theme (no client JS, no useMtb), with the RootLayout example it was missing.
  • The kebab-case note moves below the two, so the SSR paragraphs sit together.

pnpm lgtm green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtANJj1cbBZpj12mEXjgSN

@abernier
abernier force-pushed the docs/rsc-ssr-notes branch 2 times, most recently from ab881d7 to 8485a31 Compare August 18, 2026 08:57
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
abernier force-pushed the docs/rsc-ssr-notes branch from 8485a31 to 7ecdbf5 Compare August 18, 2026 08:58
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
@abernier abernier changed the title docs: correct the SSR story around <Mtb> and the RSC escape hatch docs: correct the SSR story, trim the Tailwind and shadcn sections Aug 18, 2026
@abernier abernier added the documentation Improvements or additions to documentation label Aug 18, 2026
abernier and others added 2 commits August 18, 2026 12:30
…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
abernier merged commit 645f024 into main Aug 18, 2026
6 checks passed
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
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant