Skip to content

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

  1. Re-use first. If an existing token / component / pattern fits, use it. Justify any addition.
  2. Layer correctly. Primitives in @matter/tokens. Semantic ring in the ring sheet. Component-local in the component's CSS module. See Token architecture.
  3. Dual-source discipline. Brand tokens live in both packages/brand/src/tokens/*.ts (TS) and packages/brand/src/styles/tokens.css (CSS). The drift-guard test fails CI on divergence.
  4. 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.

On this page