MD3 React

Integration

Wire MD3 tokens into your Tailwind build without fighting the cascade.

Tailwind v4

Tailwind v4 and the library share one cascade, so there's no separate "component library CSS" to reason about — just one layer order to pin once. In your Tailwind entry CSS, pin it on the first line, then import the shared layers, the components you actually use, and the token→utility mapping:

@layer theme, base, components, utilities;

@import "tailwindcss";

/* required: tokens + theming, ripple (used by most pressable components) */
@import "@brijbyte/md3-react/tokens.css";
@import "@brijbyte/md3-react/ripple.css";

/* MD3 tokens as Tailwind utilities (see below) */
@import "@brijbyte/md3-react/tailwind-tokens.css";

/* only the components you use — keeps the CSS payload minimal.
   Order matters: button.css must precede icon-button.css/fab.css/
   split-button.css/chip.css, which build on it */
@import "@brijbyte/md3-react/button.css";
@import "@brijbyte/md3-react/icon-button.css";
@import "@brijbyte/md3-react/checkbox.css";

The library uses Tailwind's own layer names — tokens in theme, component styles in components — so the standard Tailwind pin is all you need: preflight can't break the components, and any utility class you write outranks component styles without a fight. The whole docs runs on exactly that setup.

Tokens as Tailwind utilities

Forget maintaining a parallel color palette in tailwind.config — MD3's own tokens already are your design system. @brijbyte/md3-react/tailwind-tokens.css maps every system token to a Tailwind theme namespace, so bg-primary or text-title-large just work, spelled the way the tokens already are:

MD3 tokensUtilities
--md-sys-color-*bg-background, text-on-background, bg-primary, border-outline, … (all 37 roles, verbatim)
--md-sys-typescale-*text-display-largetext-label-small — each sets size, line-height, weight, and tracking
--md-sys-shape-corner-*rounded-extra-smallrounded-extra-large
--md-sys-elevation-*shadow-level0shadow-level5
--md-sys-motion-easing-*ease-standard, ease-emphasized, plus -accelerate / -decelerate variants

Because the mapping is @theme inline, each utility resolves to var(--md-sys-*) at runtime rather than a frozen value — so bg-background follows the active theme automatically. No dark: variants to write or maintain, and any future dynamic-color theming picks up for free, everywhere you already used a token utility.

<article className="rounded-large bg-surface-container-high p-6 shadow-level1">
  <h3 className="text-title-large text-on-surface">Tokens as utilities</h3>
  <p className="text-body-medium text-on-surface-variant">…</p>
</article>

Tokens as utilities

Toggle the docs theme — every class in this card is a token utility, so it follows.

Tokens as utilities

MD3 system tokens mapped to Tailwind theme namespaces — toggle the docs theme and every class below follows.

bg-primarybg-secondary-containerbg-tertiary-containerbg-error-containerborder-outline

Aliasing with the md3 CLI

Don't want to track which stylesheet each component needs — or that button.css must precede icon-button.css? The package ships an md3 CLI whose alias command writes a re-export file per component into your app, with the required CSS imported in the right order:

npx md3 alias --dir src/ui button icon-button checkbox
// src/ui/icon-button.tsx (generated)
import "@brijbyte/md3-react/button.css";
import "@brijbyte/md3-react/icon-button.css";

export * from "@brijbyte/md3-react/icon-button";

Your app then imports from its own path — import { IconButton } from "./ui/icon-button" — and the stylesheets come along automatically, ordered correctly. With this approach the per-component @import lines from the entry CSS above are unnecessary; your Tailwind entry keeps only the layer pin, tokens.css, ripple.css, and tailwind-tokens.css.

Overriding component styles

Utility overrides

Need one button a different shade? Just add a class — no fighting the library's own CSS. Utility classes win over component styles because the utilities layer is declared after components, so you never reach for !important.

Targeting variants and states

Want to re-skin every outlined button in the app, or recolor a toggle only while it's pressed? One selector does it — the styling contract is stable class names (md3-<component>-<part>, e.g. .md3-button-root) plus data-* attributes: data-variant / data-size / data-color mirror the component props, and Base UI's state attributes (data-pressed, data-checked, data-disabled, data-popup-open, …) mirror interaction state. Variants are never baked into class names, so the selector below re-skins a variant globally, and unlayered app CSS always beats the components layer — no !important or specificity hacks:

/* every outlined button in the app */
.md3-button-root[data-variant="outlined"] {
  border-color: var(--md-sys-color-primary);
  color: var(--md-sys-color-primary);
}

/* large FABs only */
.md3-fab-root[data-size="large"] {
  border-radius: var(--md-sys-shape-corner-large);
}

/* selected (pressed) toggle icon buttons */
.md3-icon-button-root[data-pressed] {
  color: var(--md-sys-color-tertiary);
}

The same attributes double as Tailwind data-attribute variants for one-off overrides, composing directly with the token utilities above — no separate CSS file needed:

<Button variant="tonal" toggle className="data-pressed:bg-tertiary data-pressed:text-on-tertiary">
  Mute
</Button>