Getting started for developers
You're a developer. Your output is code that consumes Matter's design system — typically inside apps/app, apps/web, apps/docs, or a downstream Matter consumer. This page is the five-minute onboarding.
1. Install
bun add @matter/tokens @matter/components @matter/theme @matter/iconsOr, if you're consuming through the brand layer (marketing and brand surfaces):
bun add @repo/brandThe brand package re-exports everything from @matter/components plus the brand-specific primitives (BetaPill, MatterButton, StatusPill, and friends).
2. Wire the stylesheet
The single canonical stylesheet:
// app/layout.tsx or wherever your root layout lives
import "@repo/brand/styles";This pulls the chain — @matter/tokens CSS variables, Tailwind v4 @theme inline mapping, motion recipes, the V1 → V2 transitional bridge. One import; never re-order it manually.
3. Import your first component
import { Pill, Button } from "@matter/components";
export function Hello() {
return (
<div>
<Pill tone="green">Live</Pill>
<Button variant="primary">Save</Button>
</div>
);
}Every export is documented at /components. The props table on each page is generated from the TypeScript source — what you see there is exactly what the IDE will show in autocomplete.
4. Read tokens
Three patterns in order of preference:
// 1. CSS variable — always-correct, theme-aware, zero JS cost.
<div style={{ color: "var(--fg-muted)" }} />
// 2. Tailwind utility — composed through the @matter/presets-tailwind preset.
<div className="text-fg-muted" />
// 3. TypeScript token export — only for computations, not runtime styling.
import { fgMuted } from "@matter/tokens";
const lightVariant = lighten(fgMuted, 0.1);Never inline a hex. The drift gate fails on raw color literals in MDX, and the same rule applies in your source.
5. Theme, density, forced colors
The site supports four conditions and every component renders correctly under all of them:
| Condition | Where it switches | How to test |
|---|---|---|
| Light theme | <html data-theme="light"> (default) | Default page render |
| Dark theme | <html data-theme="dark"> | Toggle from the site header chrome |
| Compact density | <html data-density="compact"> | Toggle from the header next to theme |
| Forced colors | @media (forced-colors: active) | macOS: System Settings → Display → Increase Contrast |
Your code never branches on theme — every component reads tokens that adapt automatically.
6. Graceful degradation
Matter follows the next-forge graceful-degradation pattern. Every optional dependency uses optional chaining; missing env vars silently disable features.
// Right — optional chaining
const prices = stripe?.prices.list();
// Wrong — assumes Stripe is configured
const prices = stripe.prices.list();Every package has a keys.ts that validates required env vars via Zod. The keys() function is called at the consumer's boot time, not at import time, so missing-key errors surface where they're used. Read the next-forge documentation for the broader pattern.
7. The consumer migration playbook
If you're migrating an existing surface from V1 → V2 components:
- MonoChip → Pill. Identical visual, swap the import. Tones map 1:1.
- CodeCard → CodeBlock with
headerslot. Same geometry; the header gets<EndpointBadge>+ a request-time span. - VerbPill → EndpointBadge in verb-only mode.
<EndpointBadge method="POST" />with nopathproduces the same chip. - DisplayHeading (V1) → DisplayHeading (V2). Import path changes; visual identity unchanged.
The V1 alias chain in shadcn-bridge.css keeps both versions rendering identically until your consumer fully migrates.
8. Local development
The design site itself runs at localhost:3006:
bun dev --filter designEvery change to packages/tokens, packages/components, packages/brand, or apps/design/content/design/ regenerates the agent surfaces and rebuilds. The drift gate runs on bun run --filter design check:drift — green CI means nothing fell out of sync.
9. When you're stuck
- Need a component that doesn't exist? Propose it via Governance — the bar is "appears in ≥ 3 real screens" or "fundamental primitive missing."
- Need a token that doesn't exist? Same path — proposal first, never inline.
- Component renders wrong? Check the drift gate output, then
bun run --filter design typecheck. 90% of "renders wrong" is a stale token cache. - Migrating a V1 consumer? Reach for the Authoring rules migration section first.