# Matter Design System > Canonical reference for tokens, components, patterns, and authoring rules. > Single-file bundle for LLM ingestion. Canonical site: https://design.mattermode.com > Structured payload: https://design.mattermode.com/design-system.json > JSON Schema: https://design.mattermode.com/design-system.schema.json --- # Agents Source: https://design.mattermode.com/agents Description: How AI agents consume the Matter design system. JSON manifest, JSON Schema, llms-design.txt, per-page raw MDX, version pinning. Worked examples and the contract. Matter's design system ships three agent-readable surfaces alongside the human-readable site. Every surface is fetched by URL; every surface is versioned; every surface is regenerated on every build from the canonical sources (`@matter/tokens`, `@matter/components`, the MDX corpus). This page is the canonical guide for wiring an agent to consume them. The reader is either an AI agent fetching this page as context, or a developer building an agent that consumes Matter's design system. Both audiences are served by the same content. ## The three surfaces | Surface | URL | When to fetch | |---|---|---| | **Structured manifest** | [`/design-system.json`](/design-system.json) | When the agent needs structured token / component metadata | | **JSON Schema** | [`/design-system.schema.json`](/design-system.schema.json) | Once at agent boot, for validation | | **Flat-text bundle** | [`/llms-design.txt`](/llms-design.txt) | When the agent needs full corpus context (system-prompt injection) | | **Per-page raw MDX** | `/api/raw/` | When the agent needs one page's details without the full bundle | All four are CORS-enabled and cached for 1 hour. ## Quick start (5 lines) ```ts const manifest = await fetch("https://design.mattermode.com/design-system.json").then((r) => r.json()); const version = manifest.version; // → "2.3.0" const tokens = manifest.tokens; // grouped by kind const components = manifest.components; // canonical export list const button = components.find((c) => c.name === "Button"); ``` Everything else is detail on top of these five lines. ## Manifest shape The top-level keys of `design-system.json`: | Key | Type | Use | |---|---|---| | `$schema` | URL | Points to `/design-system.schema.json` | | `version` | string | `packages/tokens/package.json#version`. Pin against this. | | `generated_at` | ISO timestamp | Deterministic from git HEAD's commit time | | `site_url` | URL | `https://design.mattermode.com` | | `tokens` | object | Keyed by kind: `colors`, `radii`, `shadows`, `motion`, `layout`, `spacing`, `typography`, `density` | | `components` | array | Every exported component with `name`, `import_path`, `source_url`, `page_url`, `has_page` | | `pages` | array | Every MDX page indexed by slug | | `recipes` | array | Every recipe page (the "real screens") | | `patterns` | array | Every pattern page (composition recipes) | | `motion_grammar` | object | `durations` + `easings` extracted from the motion tokens | | `voice` | object | `rules[]` — canonical voice constraints | | `accessibility` | object | `contract` (always `"AA"`), `focus_ring`, `reduced_motion`, `forced_colors` | Every token row carries: ```ts { name: string; // "fg" / "peach2" / "radius-md" css_var: string; // "--fg" / "--peach-2" / "--radius-md" value: string; // "#0D0D0D" / "12px" / "0 1px 2px rgba(0,0,0,0.05)" source: "matter" | "brand"; ts_path: string; // "packages/tokens/src/index.ts#colors.fg" group?: string; // "Foreground" / "Surface" / "Status" — taxonomy doc?: string; // optional inline doc string from the TS source } ``` Every component row carries: ```ts { name: string; // "Button" import_path: "@matter/components" | "@repo/brand/components"; source_url: string; // GitHub blob URL source_path: string; // relative path in the monorepo has_page: boolean; page_url?: string; // /components/button when has_page } ``` ## Validate before consuming Schema drift is the agent's leading failure mode. Validate the manifest against the JSON Schema on every fetch: ```ts import Ajv from "ajv"; import schema from "./design-system.schema.json"; const ajv = new Ajv({ strict: true }); const validate = ajv.compile(schema); const manifest = await fetch(MANIFEST_URL).then((r) => r.json()); if (!validate(manifest)) { throw new Error(`Schema drift: ${ajv.errorsText(validate.errors)}`); } ``` If your agent caches the manifest, validate the cached payload too — schema drift can happen between agent runs. ## Pin against the version The `version` field is the canonical pin point. Three patterns: ```ts // Pattern 1 — exact pin. Strict but brittle; agent breaks on every release. if (manifest.version !== "2.3.0") throw new Error("Bumped"); // Pattern 2 — minor pin via semver. Tolerates patches; surfaces minor + major. import semver from "semver"; if (!semver.satisfies(manifest.version, "~2.3.0")) throw new Error("Bumped beyond patch"); // Pattern 3 — drift detection. Allow any version, but log when it changes. if (manifest.version !== lastSeenVersion) { logger.info("Design system bumped", { from: lastSeenVersion, to: manifest.version }); lastSeenVersion = manifest.version; } ``` Choose by tolerance for design-system drift in your agent's output. ## llms-design.txt for system prompts When the agent needs the full corpus as system-prompt context: ```bash curl -s https://design.mattermode.com/llms-design.txt ``` The flat-text bundle is every MDX page concatenated, capped at ~150 KB. Inject it once into the system prompt at agent boot; cache for 1 hour. The bundle is regenerated on every build via [`build-llms-design.ts`](https://github.com/matterhq/matter/blob/main/apps/design/scripts/build-llms-design.ts). For Claude / GPT / Gemini system-prompt patterns: ```ts const designSystemContext = await fetch("https://design.mattermode.com/llms-design.txt").then((r) => r.text()); const systemPrompt = ` You are an agent generating UI code for Matter consumers. The Matter design system is documented below. Generate code that consumes the documented components and tokens; never invent tokens or components that don't appear in the manifest. ${designSystemContext} When responding, cite the component you used by its name and link (e.g. "Used from /components/pill"). `; ``` ## Per-page raw MDX When the agent needs a single page's details and you don't want 150 KB of context: ```bash curl -s https://design.mattermode.com/api/raw/components/button ``` Returns the raw MDX source as `text/markdown`. Use this for targeted lookups — the agent has already decided which component is relevant and now wants the full docs for it. ## Worked examples ### Generate a Matter-branded React component ```ts async function generateComponent(userRequest: string) { const manifest = await getManifest(); const candidates = manifest.components.map((c) => `${c.name} (${c.import_path})`); const result = await claude.messages.create({ system: `${await getDesignSystemContext()}\n\nGenerate code consuming only the listed components.`, messages: [ { role: "user", content: `Build me: ${userRequest}\n\nAvailable components: ${candidates.join(", ")}` }, ], }); return result.content; } ``` ### Score a generated screen against the system ```ts function scoreAgainstSystem(generatedSource: string, manifest: Manifest) { const componentNames = new Set(manifest.components.map((c) => c.name)); const tokenVars = new Set(manifest.tokens.colors.map((t) => t.css_var)); const issues: string[] = []; // Every import from @matter/components or @repo/brand should be in the manifest. const imports = parseImports(generatedSource); for (const imp of imports) { if (imp.from.startsWith("@matter") || imp.from.startsWith("@repo/brand")) { for (const name of imp.names) { if (!componentNames.has(name)) { issues.push(`Unknown component: ${name} from ${imp.from}`); } } } } // Every CSS var should be in the token surface. const usedVars = extractCssVars(generatedSource); for (const v of usedVars) { if (!tokenVars.has(v)) { issues.push(`Unknown CSS variable: ${v}`); } } // No raw hex literals. const hex = generatedSource.match(/#[0-9a-fA-F]{3,8}\b/g); if (hex) { issues.push(`Raw hex literals: ${hex.slice(0, 3).join(", ")}…`); } return issues; } ``` ### Detect drift between cached and live ```ts function getCachedManifest(): Manifest | null { try { return JSON.parse(localStorage.getItem("ds-manifest") ?? ""); } catch { return null; } } async function refreshIfDrifted() { const cached = getCachedManifest(); const liveHead = await fetch("https://design.mattermode.com/design-system.json", { method: "HEAD" }); const liveEtag = liveHead.headers.get("etag"); if (cached?.etag === liveEtag) return cached; const live = await fetch("https://design.mattermode.com/design-system.json").then((r) => r.json()); localStorage.setItem("ds-manifest", JSON.stringify({ ...live, etag: liveEtag })); return live; } ``` ### Cite component import paths in generated code ```ts function withCitation(component: { name: string; import_path: string; page_url?: string }) { return `Used <${component.name}> from ${component.import_path}${component.page_url ? ` (${component.page_url})` : ""}.`; } ``` ## The voice rule (must propagate to every agent) The single rule every agent must honour when generating Matter copy: > **Never compare Matter to other products.** No "Matter's analog of X," no "deliberate deviation from Y," no "mirrors Z's grammar." Describe what Matter does plainly and self-contained. Factual references to standards (WAI-ARIA roles, RFC 7807, OAuth 2.1, MCP) are fine; framing Matter's design as a comparison to a named competitor product is not. This rule lives in `manifest.voice.rules[]` so the agent can quote it verbatim: ```ts const voiceRules = manifest.voice.rules.join("\n"); const systemPrompt = `${corpusContext}\n\nWhen writing copy, follow:\n${voiceRules}`; ``` ## What the manifest doesn't have - **Rendered visuals.** The agent surface is structured; for pixel-perfect renders, fetch a [ComponentPreview](/components/button#themes) screenshot via the per-page route (when shipped). - **Compiled CSS.** Tokens surface as values, not as compiled rules. If you need compiled CSS, fetch the design site's CSS directly (not stable; subject to bundler changes). - **Figma node IDs.** Component MDX carries `` blocks — fetch the per-page raw MDX to extract these. ## When the manifest doesn't cover something The manifest is the canonical structured surface. When something isn't there: 1. **The page doesn't exist.** Propose it via [Governance](/governance) — open a PR with the missing page. 2. **The data shape isn't structured yet.** File an issue tagged `agent-payload` describing what you need. Don't synthesise design-system data. Don't invent tokens or components. If your agent is reaching for something not in the manifest, that's a signal — either the design system needs to grow, or the agent's task is outside the design system's surface. ## Versioning contract | Bump | Likely impact on the agent | |---|---| | **Patch** (`2.3.0 → 2.3.1`) | No agent action; cached manifests stay valid. | | **Minor** (`2.3.0 → 2.4.0`) | Re-fetch and re-validate; new tokens / components may be available. Existing references stay valid. | | **Major** (`2.x → 3.0`) | Re-fetch, re-validate, **re-read the changelog**. Existing references may be deprecated or removed. | The [changelog](/changelog) page is the agent's source of truth for "what changed between versions." Treat a major bump as a prompt-template review trigger. ## Endpoints reference | URL | Format | Cache | Use | |---|---|---|---| | `/design-system.json` | JSON | 1h | Structured manifest | | `/design-system.schema.json` | JSON Schema | 1h | Validation | | `/llms-design.txt` | Text | 1h | System-prompt context | | `/llms.txt` | Text | 1h | Index of pages in the bundle | | `/api/raw/` | Markdown | None | Single-page raw MDX | | `/changelog` (HTML) | HTML | Live | Human-readable release notes | Everything is CORS-enabled. No authentication required. Rate-limited per-IP at the Vercel edge. --- # Brand in action Source: https://design.mattermode.com/brand-in-action Description: Real Matter surfaces where the design system shows up — marketing hero, dashboard equity, assistant composer, API reference, docs corpus. Each card links the surface to the components and tokens it consumes. The design system is a contract, not a gallery of mocks. This page shows the contract honoured — real surfaces from `apps/web`, `apps/app`, and `apps/docs`, with the components and tokens they consume called out so you can read from "this is what I want to build" → "this is the component / token I reach for" in one sweep. ## How to read this page Each card pairs a screenshot of a real surface with: - **Components** — every documented component in the surface, linked to its page. - **Tokens** — the headline tokens (bloom variant, surface family, accent palette) the surface leans on. Every screenshot is captured at 1280×720 from a live consumer; the screenshot library lives under [`apps/design/public/brand-in-action/`](https://github.com/matterhq/matter/tree/main/apps/design/public/brand-in-action). The capture workflow is documented in that directory's `README.md`. ## Live surfaces ## What this page is The gallery is the *production* face of the design system. Every entry shows a real surface a real user sees — the marketing hero a prospective customer reads, the dashboard a founder works in every day, the API reference a developer comes back to. When a new consumer app or surface ships, add an entry here. The bar is "a representative screen that exemplifies how the design system shows up in this surface." Don't ship per-flow screenshots; one per surface is the right grain. ## What this page isn't - **Not a marketing gallery.** No mocks, no hero illustrations, no aspirational renders. Real surfaces only. - **Not a tutorial.** For the implementation walkthrough of a surface, the [Recipes](/recipes) section is the right place. - **Not a per-component catalogue.** That's [Components](/components). Brand-in-action is "how the system composes," not "what the parts look like." ## Maintenance Screenshots drift. When a consumer surface changes meaningfully (a new layout, a token re-skin, a major component swap), update the corresponding entry's screenshot. A future tranche wires this into Playwright + Chromatic for automated visual regression; until then, screenshot maintenance is on the author of the surface change. The screenshot manifest lives in [`apps/design/components/brand-gallery.tsx`](https://github.com/matterhq/matter/blob/main/apps/design/components/brand-gallery.tsx). Adding a new entry there + a new PNG under `public/brand-in-action/` ships the new card. --- # Brand Source: https://design.mattermode.com/brand Description: Mark, wordmark, lockup, tagline — the parametric identity that scales off a single `--logo-size` token. The identity is two pieces: a solid black square and the wordmark **matter** set in Funnel Display 800. They lock up off a single `--logo-size` token — every secondary measurement (mark side, gap, tagline) is a ratio of that em, so a single knob scales the whole signature. Color is always `currentColor`; the wordmark stays monochrome and inherits from its surface. Reach for canonical class hooks `.matter-lockup` / `.matter-mark` / `.matter-wordmark` / `.matter-tagline` in product — never re-implement the geometry locally. ## Philosophy **Freedom · open spaces · dreamy textures · colour · abstract.** Five words that govern every brand decision — what we put on the page, what we leave off, how the surfaces breathe, how colour earns its place. The mark and the wordmark are deliberately reduced so the surfaces around them can be expansive; the colour layer (peach, lavender, sage, amber) carries the dream. These five words are the **canonical brand frame**. Any visual decision — a layout, a colour application, a component composition, a page hero — should land obviously on at least three of them. If a design doesn't, it's drifting; reach for fewer elements, more space, more saturated colour, or a more abstract surface, in that order. ## Tone **Precise · confident · humble · future.** The four-word tone Matter writes in, across marketing, docs, dashboard, OpenAPI descriptions, error messages, CMS body content. Long form at [Usage / Voice — Tone](/design/usage/voice). Brand and tone are read together: the philosophy governs the surface, the tone governs the words on it. ## Identity at hero scale
## Five sub-pages - **Mark** — the solid square; safe area; minimum size. - **Wordmark** — Funnel Display 800, lowercase, locked tracking. - **Lockup** — parametric mark + wordmark with the `--logo-asc-ratio` / `--logo-gap-ratio` knobs. - **Tagline** — canonical token strings: title-case, all-caps, mid-dotted mono. - **Tokens** — the full surface of `--brand-*` and `--logo-*` tokens. --- # Lockup Source: https://design.mattermode.com/brand/lockup Description: Mark + wordmark, parametric — one `--logo-size` token scales the whole signature. The lockup is parametric. Set `font-size` on `.matter-lockup` (or use the presets `.matter-lockup--app/--nav/--hero/--print`) and the mark side, gap, and tagline scale together off a single em. ## Five named presets
## Light and dark
## Em ratios | Token | Ratio | Role | |---|---|---| | `--logo-asc-ratio` | `0.78` | Mark side / em | | `--logo-gap-ratio` | `0.20` | Gap between mark and wordmark / em | | `--logo-tag-italic-ratio` | `0.20` | Title-case tagline / em | | `--logo-tag-caps-ratio` | `0.105` | All-caps tagline / em | ## Surface presets | Token | Value | Use | |---|---|---| | `--logo-size-app` | `18px` | App-shell topbar | | `--logo-size-nav` | `26px` | Marketing nav | | `--logo-size-print` | `40px` | Print and PDF | | `--logo-size-hero` | `96px` | Brand splash / OG images | Below 28 px, drop to mark-only. ## Markup ```html ```
Apply `.matter-lockup--app` / `--nav` / `--hero` / `--print` and let the ratios resolve the geometry.
Don't hard-code the gap in px. Every secondary measurement is a ratio of the em.
--- # Mark Source: https://design.mattermode.com/brand/mark Description: A solid square. Color is `currentColor` — never a fixed hex. The mark is a single solid square sized by `--logo-asc-ratio` × `--logo-size` (default `0.78 × em`). It carries no rounded corners, no inner cut-out, no proprietary glyph — the square *is* the identity. ## Mark at every preset scale
hero · 96
print · 40
nav · 26
app · 18
favicon · 16
## Color `currentColor` only. The mark inherits ink from its surface: - Light surface → `var(--fg)` = `#0D0D0D`. - Dark surface → `#F4F1EC` (warm white). Never pure `#FFF`. Don't apply a brand orange. The wordmark is monochrome by rule. } dark={} /> ## Minimum size - Screen: 16 px (favicon). - Below 28 px, drop to mark-only — the wordmark loses optical weight. - Print: 8 mm. ## Class hook `.matter-mark` — sized as `width: calc(var(--logo-size) * var(--logo-asc-ratio))`.
Use `.matter-mark` and let it inherit color from its parent surface.
Don't fork a SVG to recolor the mark — the system has one source of truth.
--- # Tagline Source: https://design.mattermode.com/brand/tagline Description: The brand tagline lives in tokens, not markup. Three forms ship — title case, all caps, mid-dotted mono. The brand tagline lives in CSS variables, not component markup. Read it via `var(--brand-tagline)` in CSS, or `getComputedStyle(document.documentElement).getPropertyValue('--brand-tagline')` in JS. ## Three forms ### Title case — prose ### All caps — banner ### Mid-dotted mono — navigation rails, footer ## Light and dark — banner form
## Token bindings | Token | Example | |---|---| | `--brand-tagline` | `"Complete your mission"` | | `--brand-tagline-caps` | `"COMPLETE YOUR MISSION"` | | `--brand-tagline-mono` | `"COMPLETE · YOUR · MISSION"` | ## Type rule Funnel Display is **reserved for the wordmark**. The tagline never uses Funnel Display — it renders in Geist Sans (title case) or Geist Mono (banner / mid-dotted). The tagline never competes with the wordmark. ## Auto-caps utility `.matter-tagline--auto-caps` renders the all-caps tagline from the token via `::before { content: var(--brand-tagline-caps) }`. No hard-coded string in markup — change the token, ship everywhere.
Wire components to `var(--brand-tagline)` so a single edit propagates.
Don't paste the literal string. The token is the contract.
--- # Brand tokens Source: https://design.mattermode.com/brand/tokens Description: Voice, wordmark type, lockup geometry, surface presets. Change here, ship everywhere. ## The system, materialized
Every preset above resolves from a single `--logo-size-*` token. Edit the token, every consuming surface follows. ## Voice · copy | Token | Value | |---|---| | `--brand-name` | `"matter"` | | `--brand-name-cap` | `"Matter"` | | `--brand-tagline` | `"Complete your mission"` | | `--brand-tagline-caps` | `"COMPLETE YOUR MISSION"` | | `--brand-tagline-mono` | `"COMPLETE · YOUR · MISSION"` | | `--brand-domain` | `"matter.com"` | ## Wordmark · type | Token | Value | |---|---| | `--logo-font` | `var(--font-funnel-display), 'Funnel Display', Arial Black, Helvetica Neue, sans-serif` | | `--logo-weight` | `800` | | `--logo-tracking` | `−0.045em` | | `--logo-leading` | `0.78` | ## Lockup · geometry (em ratios) | Token | Ratio | Role | |---|---|---| | `--logo-asc-ratio` | `0.78` | mark / em | | `--logo-gap-ratio` | `0.20` | gap / em | | `--logo-tag-italic-ratio` | `0.20` | title-case tag / em | | `--logo-tag-caps-ratio` | `0.105` | all-caps tag / em | ## Surface presets (px) | Token | Value | Use | |---|---|---| | `--logo-size-app` | `18px` | app shell top | | `--logo-size-nav` | `26px` | marketing nav | | `--logo-size-hero` | `96px` | brand splash | | `--logo-size-print` | `40px` | print & PDF | ## Production usage Drop the canonical class hooks anywhere: ```html ``` `.matter-tagline--auto-caps` renders the all-caps tagline from the token via `::before { content: var(--brand-tagline-caps) }` — no hard-coded string in markup. Color is always `currentColor`.
Edit the token; the wordmark, tagline, and every surface follow.
Don't bypass the tokens. Hard-coding the brand string in JSX is a drift bug waiting to happen.
--- # Wordmark Source: https://design.mattermode.com/brand/wordmark Description: **matter** in Funnel Display 800 — lowercase, tight tracking, locked leading. ## At every preset scale
hero · 96
print · 40
nav · 26
app · 18
## Light and dark surfaces } dark={} /> ## Type | Token | Value | |---|---| | `--logo-font` | `var(--font-funnel-display), 'Funnel Display', Arial Black, Helvetica Neue, sans-serif` | | `--logo-weight` | `800` | | `--logo-tracking` | `−0.045em` | | `--logo-leading` | `0.78` | ## Casing Always lowercase. Never sentence case. Never uppercase. There's an optical `tt` ligature pull (`margin-left: -0.02em`) that lets the two t's overlap naturally — the class hook `.tt-pull` carries this. ## Pairing - Standalone for marketing surfaces. - Right of the mark for the product topbar lockup — see [Lockup](/brand/lockup). ## Class hook `.matter-wordmark` — applies the font, weight, tracking, and leading. Color inherits from the parent.
Use `.matter-wordmark` so the optical tracking and ligature pull stay correct.
Don't substitute another sans family at the wordmark. Funnel Display is reserved for this one element.
--- # Changelog Source: https://design.mattermode.com/changelog Description: Whats landed in each release of @matter/tokens, @matter/components, @repo/brand, and the design site. Derived from git history at build time. What's landed in each release of the design-system packages. The feed below is derived from git history at build time via [`build-changelog.ts`](https://github.com/matterhq/matter/blob/main/apps/design/scripts/build-changelog.ts) — every commit touching `packages/tokens`, `packages/components`, `packages/brand`, or `apps/design` is classified by conventional-commit prefix (`feat:` → Added, `fix:` → Fixed, `refactor:`/`perf:`/`chore:` → Changed, `docs:` → Docs) and grouped by release. Breaking changes appear with a left rail, a `BREAKING — ` prefix, and a tonal background. They're detected from `!` in the commit prefix or `BREAKING CHANGE:` in the body. ## Subscribing programmatically Agents and downstream consumers should pin against the `version` field in [`/design-system.json`](/design-system.json). When the version changes, fetch the changelog page (or the underlying `apps/design/.generated/changelog.json`) to enumerate the deltas. ```bash curl -s https://design.mattermode.com/design-system.json | jq '.version' # → "2.3.0" ``` Humans (and feed readers) can subscribe to [the RSS feed](/changelog/rss.xml) to track releases without polling. ## Release feed ## Conventions We follow Conventional Commits in subject lines: | Prefix | Meaning | Maps to | |---|---|---| | `feat:` / `feature:` / `add:` | New capability | Added | | `fix:` | Bug fix | Fixed | | `refactor:` / `perf:` / `chore:` | No external behaviour change | Changed | | `deprecate:` | Marked for removal in a future release | Deprecated | | `remove:` | Removed in this release | Removed | | `docs:` | Documentation-only change | Docs | | `!:` or `BREAKING CHANGE:` in body | Breaking change | Tagged regardless of prefix | Scopes (`feat(tokens):`, `fix(components):`) are extracted into the per-row package badge. ## How a version gets cut Today the design system rolls one release per `packages/tokens/package.json#version` bump. The bump is a deliberate act: 1. A maintainer reviews the diff since the last cut. 2. Tokens / components / brand bumps happen together (the three packages release as a unit so consumers stay in sync). 3. The version field in `packages/tokens/package.json` bumps via semver (major for breaking, minor for additive, patch for fixes). 4. The deterministic timestamp in `design-system.json#generated_at` records the moment. The [governance page](/governance) covers the proposal → review → bump workflow in more detail. --- # AppBreadcrumb Source: https://design.mattermode.com/components/app-breadcrumb Description: Dashboard breadcrumb stack — ordered crumbs separated by slim diagonal slashes, last crumb is aria-current. Mono crumbs render in Geist Mono for resource IDs and file paths. Right slot accepts a trailing affordance (context-menu trigger, action button). ## Hero example ## When to use Reach for AppBreadcrumb at the top of every dashboard page that sits inside the hierarchical navigation — `entities/ent_… › Filings › Delaware AR 2026`. The trail tells the user where they are and how to back out one level. The component handles three crumb shapes: plain (no `href` or `onClick`), interactive link (`href` set), interactive button (`onClick` set). The last crumb is always the current page and renders as a non-interactive `` with `aria-current="page"`. Don't reach for AppBreadcrumb for non-hierarchical navigation — the trail implies "you got here by descending through these ancestors." For lateral tabs use [SlidingPill](/components/sliding-pill); for cross-domain pivots use a route-grammar nav primitive. Don't use AppBreadcrumb on the marketing site either — the dashboard hierarchy doesn't exist there.
**Do** — render AppBreadcrumb at the top of every dashboard detail page. The trail anchors the user's mental model.
**Don't** — render AppBreadcrumb on a top-level page (no ancestors). One crumb is just a page title.
## Anatomy Three structural parts: 1. **`