Internal reference for contributors and audits — not shipped in the npm
package. It records the authoritative sources and the cross-cutting M3
Expressive values every component builds on. Per-component spec values and
their accepted gaps live in docs/components/.
When sources disagree, the higher one wins (Material Web is pre-Expressive in places, so it is a low-priority reference except for web technique):
- m3.material.io token tables (
/components/<x>/specs) — themd.comp.*token sets: colors by role and state, dp measurements, state-layer opacities. The site is client-side rendered, so read it with a real browser (expand the token tables and the "Toggle view" value mode); plain fetching does not work. tokens.xmlfrom material-components-android — values the site does not publish.- Jetpack Compose Material3 (androidx) — choreographies and springs; prefer published tokens over hard-coded values.
- Guideline videos sampled frame by frame for motion.
@material/web(v0_192) — only for classic M3 components and web techniques; it predates Expressive.- Guideline prose.
Measure the real implementation (computed styles, getBoundingClientRect,
rAF sampling) before changing anything, and confirm motion in both
directions.
- Official M3 tokens at runtime:
--md-sys-color-*(full 49-role scheme),--md-sys-shape-corner-*,--md-sys-elevation-level1..5,--md-ref-typeface-brand/plain, the motion easings/durations. :rootis the light scheme;.dark, [data-theme="dark"]is dark. Both are verbatim Material Theme Builder exports — a new export pastes 1:1 over a color block (same role order).theme.cssmaps utilities to tokens with@theme inlineso thevar()s resolve per element and follow[data-theme]scopes at runtime. The--text-*scale is static in@theme.useMaterialTheme()(no Provider) reads/writesdata-themeon<html>, persists tolocalStorage("md-theme"), toggles.dark, and syncs across instances and tabs.
Never use *-hover tokens. Interaction states are M3 state layers of
currentColor: hover 8%, focus/pressed 12% (the v0_192 pins in
:root; several newer component sets publish 8/10/10 — documented per
component, not changed globally pending a single audit).
Two layers, like @material/web's ripple (isolation: isolate):
::after— hover/focus tint (color-mixofcurrentColor, 15ms linear).::before— the press ripple: a radial surface whose geometry is animated from the pointer byutils/_ripple.ts(grows ~450ms standard); CSS handles the fades (105ms in, 375ms linger). Keyboard/no-JS falls back to a centered tile.
.state-layer is the generic class; buttons/chips/FAB bake it in; .peerLayer
serves inputs driven by a .peer. All hand-written :hover lives under
@media (hover: hover) so a touch tap does not leave a sticky hover tint.
Levels 1–5 as layered shadows (--md-sys-elevation-level* / the shadow-mm-*
utilities). Used only by FAB, dialog, menus, sheets, snackbar and the
elevated card/button/chip. Interactive elevation transitions at 280ms
emphasized on buttons/FABs; chips snap (no elevation transition), matching mw.
Overlays layer through one documented scale of --md-sys-z-* tokens (defined
in styles.css :root; theme-independent, so they are not duplicated in
the dark scheme), consumed as full Tailwind literals like
z-[var(--md-sys-z-menu)]. Ascending paint order:
| Token | Value | Used by |
|---|---|---|
--md-sys-z-base |
0 | default content plane |
--md-sys-z-raised |
10 | in-flow raised bits (FAB-in-toolbar, indicators) |
--md-sys-z-sticky |
20 | sticky/stacked in-flow chrome (avatar overlaps) |
--md-sys-z-dropdown |
30 | anchored inline popovers (Select, Search, docked pickers) |
--md-sys-z-menu |
50 | portaled menus & popovers (Dropdown, OverflowMenu, SplitButton, Menu.Sub, Combobox) |
--md-sys-z-tooltip |
55 | tooltips |
--md-sys-z-modal |
60 | dialogs, sheets, modal rail + their scrims |
--md-sys-z-snackbar |
70 | toasts |
Menus sit below modals: a page-level menu yields to a dialog/sheet scrim,
while a menu opened inside a dialog still paints above it (both portal to
document.body; the menu mounts later). Override any token to slot custom
chrome between layers. A separate, clearly-local negative set
(--md-sys-z-state-press/-hover, --md-sys-z-nav-indicator) lives inside
isolated state-layer/nav hosts and never interacts with this scale.
- Emphasized
cubic-bezier(0.2, 0, 0, 1); the full standard/emphasized (+accelerate/decelerate), legacy, linear easings and the 16--md-sys-motion-duration-*live in:root. - Expressive springs sampled from Compose as CSS
linear():--md-sys-motion-spring-fast-spatial(use with 350ms transitions) and--md-sys-motion-spring-default-spatial(450ms; navigation indicators, the rail morph). Other springs are approximated with beziers. - Overlays animate in (emphasized-decelerate ~400ms) and out (emphasized-accelerate ~200ms) and stay mounted during their exit.
- Shape morphs (round↔square, split button, connected group) interpolate the
real pixel radius (
h/2), nevercorner-full(9999px would collapse the neighbouring corner and snap).
15 styles as text-* utilities. Note body-large (16/24/+0.5, w400) and
title-medium (16/24/+0.15, w500) are not interchangeable.
- Disabled: container
on-surface/12+ contenton-surface/38(outlined: 12% border); elevated surfaces drop their shadow. No globalopacity. - Focus ring: 3px
secondary, offset 2, animated entrance (0→8px in 150ms → 3px in 450ms emphasized). Menu items are the exception — no ring, state layer only (roving focus would otherwise re-fire the ring each step).
The kit is M3 Expressive only: anything the spec marks "no longer
recommended" is not shipped (see the README "Expressive-only" section).
Where the spec keeps more than one valid style, the recommended one is the
default and the alternative stays available (List plain, Search divided,
Progress/Circle wavy). When an audit finds a newly-deprecated variant,
remove it (don't document it as legacy) and update the docs.
Some spec details are intentionally not implemented (e.g. drag states, the
adaptive arrangement of toolbars, full-screen calendar scrolling). Each is
recorded as an "accepted gap" in the relevant docs/components/*.md with the
rationale, so they read as deliberate decisions rather than omissions.