Agents
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 | When the agent needs structured token / component metadata |
| JSON Schema | /design-system.schema.json | Once at agent boot, for validation |
| Flat-text bundle | /llms-design.txt | When the agent needs full corpus context (system-prompt injection) |
| Per-page raw MDX | /api/raw/<slug> | 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)
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:
{
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:
{
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:
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:
// 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:
curl -s https://design.mattermode.com/llms-design.txtThe 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.
For Claude / GPT / Gemini system-prompt patterns:
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 <Pill> 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:
curl -s https://design.mattermode.com/api/raw/components/buttonReturns 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
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
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
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
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:
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 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
<FigmaLink node="…">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:
- The page doesn't exist. Propose it via Governance — open a PR with the missing page.
- The data shape isn't structured yet. File an issue tagged
agent-payloaddescribing 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 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/<slug> | 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.
Matter Design System
Tokens, components, and authoring rules for every Matter surface — designed to be consumed by designers, developers, and agents from a single source.
Brand in action
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.