Authoring rules
Follow these rules so every new screen lands consistently. They apply equally to designers in Figma, developers in CSS/JSX, and agents generating UI from prompts.
Type & emphasis
Don't — use italics, anywhere. No
font-style: italic, no Instrument Serif accent, no italicized <em>. The system ships zero italic styles across web, app, and docs. For emphasis use font-weight: 500 or a stronger ink token; for accent moments use scale, color, or the mono family — never a slanted face.Do — use weight + color for emphasis.
<em> renders upright at weight 500. The dimmed-tail technique (see Display emphasis) is the only display-level accent.Tokens & values
Do — use tokens for every value. If you find yourself writing a literal hex (other than
0,0,0 / 255,255,255 alpha math), add a token first. Every color in this system traces back to @matter/tokens.Do — use raw alphas through tokens. Reach for
--ink-4, --paper-62, --paper-85. If you need an alpha that isn't on the ramp, add it to the ramp first.Bloom
Don't — invent new bloom families. Peach, lavender, sage, amber. That's it. New visual moments compose from these four — adjusting opacity, position, or layering — never new hue families.
Do — tag every primary button with a glow tint. Pair
.btn-primary with .bloom-* or .glow-* so the hover swell matches the panel it sits on. Default peach is correct only inside peach panels.Buttons
Don't — cross app and marketing button scales. Use
.btn-app-* in the app, .btn-* in marketing & docs. They look almost identical at small sizes; the difference is hit-target intent. Mixing yields awkward chrome.Glass
Do — pick glass by density, not surface. One recipe across web, app, and docs.
.glass-soft for sidebar nav, .glass for default cards, .glass-strong for active panes and modals. Differ by opacity only — same blur, same border, same shadow.Don't — hand-roll a new glass recipe. One recipe runs everywhere. If a surface needs more or less weight, change the opacity stop — don't fork blur, border, or shadow values.
API surface
Do — keep PUT and PATCH the same color. Both render in orange via
.m-put / .m-patch. They're idempotent vs. partial mutations of the same resource — same family, same tint.Do — use
.sheet-overlay for any "zoom in on one thing" moment. Modal dialogs, endpoint detail, action confirmation — all the same overlay primitive with a method-tinted card. Don't roll a custom backdrop blur.Status
Don't — rename
.status-* to generic semantics. The classes are domain-named (signed / pending / draft / filed) because the data layer uses those terms. If you need a generic success/warning ramp, add new classes alongside — don't repurpose these.Voice & copy
Do — sentence case everywhere. Two-word Title Case allowed only on buttons.
Don't — compare to other products. No "Matter's analog of X" framing in marketing, OpenAPI descriptions, or docs. Describe what Matter does plainly and self-contained. Factual references to standards (RFC 7807, OAuth 2.1, MCP) are fine.
Don't — use emoji. Two allowed Unicode glyphs only: ✺ (the spark) and › (chevron-right).
Architecture & dual-source
- Re-use first. If an existing token / component / pattern fits, use it. Justify any addition.
- Layer correctly. Primitives in
@matter/tokens. Semantic ring in the ring sheet. Component-local in the component's CSS module. See Token architecture. - Dual-source discipline. Brand tokens live in both
packages/brand/src/tokens/*.ts(TS) andpackages/brand/src/styles/tokens.css(CSS). The drift-guard test fails CI on divergence. - One PR per shape. A token + its component update + its design-system page edit ride together.
How to consume
Import the canonical CSS chain at the top of any stylesheet:
@import "@matter/tokens/css";
@import "@matter/tokens/css/canonical";
@import "@matter/tokens/css/ring";
@import "@matter/components/styles.css";Reference tokens via var(--token-name); never inline a literal.
Usage
Authoring rules, motion grammar, voice & casing, the accessibility contract, density, and the live-patterns ledger.
Motion grammar
Four named durations, one default ease, a named easing for every directional case. Bloom keyframes respect reduced-motion. Single source of truth across web, app, and docs.