AI Engineering Lab
I define tokens, components and rules once. Your team uses them in React, Angular, Vue, Swift or Kotlin, and your agents read them instead of guessing: less repeated code, fewer tokens spent and fewer fixes in review.
This page is the proof: one source of truth, rendered for people and published for agents. AI speeds up the implementation; the architecture, accessibility and final review stay with me.
Everything on this page comes from the same tokens and specs. No AI API, no database.
For your team
- One component, five stacks: React, Angular, Vue, Swift and Kotlin
- Change a token once and every app follows
- Specs with usage and accessibility rules built in
For your agents
- Spec, tokens and code from a public JSON registry
- Rules to follow instead of styles to guess
- A check that catches hardcoded values before review
One system, every stack.
Tokens and specs are written once. Style Dictionary turns tokens/lab.tokens.json into CSS variables, SwiftUI and Jetpack Compose, and every stack below gets the same Card from the same templates. Change the primary colour and all five follow.
Change primary color
#E23A0DSame token as the playground's Accent.
From the playground
Variantelevated
Radius8px
Pick Card in the playground to change the variant.
One change, every output
tokens.cssReact · Angular · Vue
--ds-color-primary: #e23a0d;Tokens.swiftSwift
public static let colorPrimary = Color(red: 0.886, green: 0.227, blue: 0.051, opacity: 1)Tokens.ktKotlin
val colorPrimary = Color(0xffe23a0d)
Primary is #E23A0D in React, Angular, Vue, Swift and Kotlin.
The image is a placeholder.
import type { CSSProperties } from "react"import { Button } from "./Button"import { Card, cardParts } from "./Card" // The card in the lab: an image, a title and a primary Button as its action. const media: CSSProperties = { width: "100%", aspectRatio: "16 / 9", objectFit: "cover", borderRadius: "calc(var(--ds-radius-card) / 2)",} export function CardExample() { return ( <Card variant="elevated" size="md"> <img src="/cover.jpg" alt="" style={media} /> <h3 className={cardParts.title}>Tokens, not hex codes</h3> <Button variant="primary">Read the docs</Button> </Card> )} Spec
specs/card.spec.json · What the component is, how to use it and which tokens it reads. The code above takes its variants, sizes and defaults from it.
{ "$schema": "./schema/component-spec.schema.json", "name": "Card", "description": "A surface that groups related content, such as a title, a short text and one action.", "source": "components/lab/demo-components.tsx (styles: RECIPES.card in lib/lab/playground.ts)", "element": "article", "extends": "HTMLAttributes<HTMLElement>", "whenToUse": [ "Group related content with at most one main action: a project, an article, a setting summary.", "Show several items of the same kind side by side, each in its own Card." ], "whenNotToUse": [ "As a clickable surface: Card has no button role or keyboard handling. Put a link or a Button inside it instead.", "For page layout or whole sections: use plain layout elements." ], "props": [ { "name": "variant", "type": "enum", "values": [ "elevated", "outlined", "filled" ], "default": "elevated", "required": false, "description": "Surface style: shadow, hairline border or a tint of color.primary." }, { "name": "size", "type": "enum", "values": [ "sm", "md", "lg" ], "default": "md", "required": false, "description": "Padding and maximum width, from RECIPES.card.sizes." }, { "name": "disabled", "type": "boolean", "default": false, "required": false, "description": "Sets aria-disabled, dims the card to 50% and blocks pointer events." }, { "name": "children", "type": "node", "default": null, "required": false, "description": "The content, styled with cardParts (eyebrow, title, body, action)." }, { "name": "className", "type": "string", "default": null, "required": false, "description": "Extra classes, merged after the recipe with cn()." } ], "variants": [ "elevated", "outlined", "filled" ], "sizes": [ "sm", "md", "lg" ], "states": [ { "name": "default", "description": "elevated: color.surface with a soft shadow. outlined: transparent with a color.border hairline. filled: color.surface tinted 10% with color.primary." }, { "name": "hover", "description": "elevated: deeper shadow. outlined: the border turns color.primary. filled: the tint grows to 16%. data-force=\"hover\" pins this state for docs and visual tests." }, { "name": "disabled", "description": "aria-disabled=\"true\", 50% opacity and no pointer events." } ], "tokens": [ { "path": "color.surface", "usage": "elevated background, and the base of the filled tint." }, { "path": "color.text", "usage": "Text colour." }, { "path": "color.muted", "usage": "cardParts.body text." }, { "path": "color.primary", "usage": "Eyebrow and action text, outlined hover border, filled tint, action focus ring." }, { "path": "color.border", "usage": "outlined border." }, { "path": "radius.card", "usage": "Corner radius; the action's focus ring uses half of it." }, { "path": "space.sm", "usage": "Padding at size sm (the web recipe's p-4 has the same value)." }, { "path": "space.md", "usage": "Padding at size md (p-5, same value)." }, { "path": "space.lg", "usage": "Padding at size lg (p-6, same value)." }, { "path": "fontSize.title", "usage": "cardParts.title text (text-base, same value)." }, { "path": "fontSize.body", "usage": "cardParts.body and cardParts.action text (text-sm, same value)." } ], "accessibility": { "role": "article, from the native <article> element. The Card itself is not interactive.", "keyboard": [ "The Card is not focusable; Tab reaches the links or buttons inside it.", "When disabled, also take inner actions out of the tab order (tabIndex={-1}): aria-disabled and pointer-events do not do that." ], "focus": "The action (cardParts.action) shows a 2px color.primary ring on focus-visible. Inner Buttons keep their own ring.", "contrast": "Title and body text at least 4.5:1 against the card background in every variant (WCAG 2.2 AA, 1.4.3).", "labels": "Start the content with a heading (cardParts.title) so the article can be found by heading navigation." }, "composition": [ "Content goes inside as children, styled with cardParts: eyebrow, title, body and action.", "cardParts.action styles a link. A Button can be the action instead, as in the 'One system, every stack' Card, which composes an image, a title and a primary Button." ], "example": { "language": "tsx", "code": "<Card variant=\"elevated\" size=\"md\">\n <p className={cardParts.eyebrow}>Design system</p>\n <h3 className={cardParts.title}>Tokens, not hex codes</h3>\n <p className={cardParts.body}>Components read semantic tokens.</p>\n <a href=\"/system\" className={cardParts.action}>Read the docs</a>\n</Card>" }}tokens/lab.tokens.jsonSource of truth
The source file, with your primary and radius edits applied. Every stack above is built from it.
{ "color": { "surface": { "$type": "color", "$value": "#1B1C21", "$description": "Card background" }, "text": { "$type": "color", "$value": "#F2F2F4", "$description": "Primary text on surface" }, "muted": { "$type": "color", "$value": "#A0A0AB", "$description": "Secondary text: card body, hints, placeholders" }, "primary": { "$type": "color", "$value": "#E23A0D", "$description": "Brand accent: buttons, links, focus" }, "onPrimary": { "$type": "color", "$value": "#111111", "$description": "Text on primary, picked for contrast" }, "border": { "$type": "color", "$value": "#2E3037", "$description": "Hairlines and outlined variants" } }, "radius": { "card": { "$type": "dimension", "$value": "0.5rem" }, "button": { "$type": "dimension", "$value": "0.5rem" }, "input": { "$type": "dimension", "$value": "0.5rem" } }, "space": { "sm": { "$type": "dimension", "$value": "1rem" }, "md": { "$type": "dimension", "$value": "1.25rem" }, "lg": { "$type": "dimension", "$value": "1.5rem" } }, "fontSize": { "title": { "$type": "fontSize", "$value": "1rem" }, "body": { "$type": "fontSize", "$value": "0.875rem" } }}Change the design, read the code.
A designer or developer tunes the component and gets code that matches. Every control updates the live component and the code at the same time; the preview renders the exact React component shown in the panel.
Controls
Component
Variant
Size
Theme
State
Accent
#E23A0Dimport { forwardRef, type ButtonHTMLAttributes } from "react"import { cn } from "@/lib/utils" const base = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--ui-radius)] font-medium transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-accent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ui-canvas)] disabled:cursor-not-allowed disabled:opacity-50" const sizes = { sm: "h-8 px-3 text-[13px]", md: "h-10 px-4 text-sm", lg: "h-12 px-5 text-base",} as const const variants = { primary: "bg-[var(--ui-accent)] text-[var(--ui-on-accent)] enabled:hover:bg-[var(--ui-accent-hover)] data-[force=hover]:bg-[var(--ui-accent-hover)]", secondary: "border border-[var(--ui-border)] bg-[var(--ui-surface)] text-[var(--ui-fg)] enabled:hover:border-[var(--ui-accent)] data-[force=hover]:border-[var(--ui-accent)]", ghost: "bg-transparent text-[var(--ui-accent)] enabled:hover:bg-[color-mix(in_srgb,var(--ui-accent)_12%,transparent)] data-[force=hover]:bg-[color-mix(in_srgb,var(--ui-accent)_12%,transparent)]",} as const export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & { variant?: keyof typeof variants size?: keyof typeof sizes} export const Button = forwardRef<HTMLButtonElement, ButtonProps>( ({ variant = "primary", size = "md", type = "button", className, ...props }, ref) => ( <button ref={ref} type={type} className={cn(base, sizes[size], variants[variant], className)} {...props} /> ),)Button.displayName = "Button"Built for agents, not just people.
The same system is published as a public registry, so an agent reads the right spec, tokens and code instead of guessing from screenshots or writing the component again.
What it saves, measured on this page
- What an agent reads to build the Card and Button for Angular: the registry's answer, not the source files.
- 22 KBinstead of 49 KB
- The Angular Card component. Its CSS (104 lines) is written once and shared by React, Angular and Vue.
- 32 lines
- Caught by validate_usage in the answer written without the system, before anyone reviews it.
- 7 issues
Source files compared
AGENTS.md, tokens/lab.tokens.json, specs/card.spec.json, specs/button.spec.json, lib/lab/stacks.ts, lib/lab/component-css.ts, lib/lab/templates/shared.ts, lib/lab/templates/card.ts, lib/lab/templates/button.ts
Measured on this demo when the page is built. Not a benchmark of a real project.
Public registry
LiveStatic, read-only JSON, generated at build time from the same tokens, specs and templates. CORS is open, so any tool can fetch it.
GET/r/index.json
Every component, its variants and sizes, and the supported stacks.
GET/r/tokens.json
The full token set, with values and their CSS, Swift and Kotlin names.
GET/r/rules.json
The agent rules: AGENTS.md plus each spec's accessibility rules.
GET/r/card.json?stack=angular
One component: spec, tokens and code. Stacks: react, angular, vue, swift, kotlin.
The MCP below is a conceptual demo of what I would set up for your team: no MCP server runs on this site. The public registry above is the part that works today.
MCP demo
IllustrativeHow a developer would connect an MCP server in Claude Code (.mcp.json) or Cursor (.cursor/mcp.json). The address is a placeholder: there is no server behind it.
{ "mcpServers": { "design-system": { "type": "http", "url": "https://design-system.example/mcp" } }}The requests are a script; the responses are real, computed in your browser from the same core that serves /r.
Using the design system MCP, build a settings card with an image and a save button for my Angular app.
Before and after
The same request, answered without and with the design system.
Without the design system
Illustrative · written by handA typical generic answer: hardcoded colours and sizes, a div as a button (no role, no keyboard), and a placeholder instead of a label.
<!-- settings-card.component.html --><div class="card" style="background: #1e1e1e; border-radius: 12px; padding: 20px"> <img src="cover.jpg" /> <h3 style="color: #ffffff; font-size: 18px">Notification settings</h3> <input placeholder="Email" style="border: 1px solid #333333" /> <div class="btn bg-blue-600" style="color: #ffffff; padding: 10px 16px" (click)="save()"> Save </div></div>validate_usage on its props
Buttoninvalid
- error
className ·Palette colour class bg-blue-600 bypasses the tokens. - error
style.color ·Hardcoded colour #ffffff. - error
style.padding ·Hardcoded size 10px. - error
style.padding ·Hardcoded size 16px.
Inputinvalid
- error
style.border ·Hardcoded colour #333333. - error
style.border ·Hardcoded size 1px. - error
label ·label is required and rendered as a visible <label for>. Do not use the placeholder as the label. The hint is announced through aria-describedby.
With the design system
Generated from the registryCard and Button from the registry, styled only with tokens, with a real <button> and the spec's focus styles.
import { Component } from "@angular/core"import { ButtonComponent } from "./button.component"import { CardComponent } from "./card.component" // The card in the lab: an image, a title and a primary Button as its action.@Component({ selector: "app-card-example", standalone: true, imports: [CardComponent, ButtonComponent], template: ` <ds-card variant="elevated" size="md"> <img class="media" src="/cover.jpg" alt="" /> <h3 class="ds-card__title">Notification settings</h3> <button dsButton variant="primary">Save changes</button> </ds-card> `, styles: ` .media { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: calc(var(--ds-radius-card) / 2); } `,})export class CardExampleComponent {} validate_usage on its props
Buttonvalid
From design to production, with someone accountable.
The workflow behind the system: what AI speeds up, what I decide, and how a vague first pass becomes a reviewed component.
Four steps, one person accountable.
How a component moves from a design to production. Pick a step to see what AI speeds up and what I decide.
Objective
Turn the Figma component into an explicit spec that both people and agents can implement without guessing.
Example input
specs/button.md
component: Buttonvariants: primary | secondary | ghostsizes: sm 32 · md 40 · lg 48tokens: --ui-accent --ui-on-accent --ui-radiusstates: default · hover · focus-visible · disabled · loadinga11y: native <button>, visible focus ring, aria-busy while loadingapi: extends ButtonHTMLAttributes, forwardRef, no boolean flagsWhat AI helps with
- Reading variables and variants from Figma through Figma MCP
- Drafting the spec and listing edge cases I might miss
- Recalling past decisions and conventions stored in Engram
What I review or decide
- The component API and naming
- Which states exist, and what is out of scope
- How design values map to existing tokens
Risks and how I handle them
- The spec copies pixel values from FigmaMap every value to a token before implementation
- States are forgotten (loading, disabled, focus)A fixed state checklist in every spec
Output
A reviewed spec with acceptance criteria.
Same button, two levels of intent.
A first pass from an underspecified prompt, next to the version after review. The first pass is not bad because AI wrote it, but because the request was vague. With the spec from step 1, the first draft lands much closer to the refined one.
Highlighted lines in the refined version: Accessibility
import { forwardRef, type ButtonHTMLAttributes } from "react"import { cn } from "@/lib/utils"import { Spinner } from "./spinner"import { buttonRecipe, type ButtonSize, type ButtonVariant } from "./button.recipe" export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & { variant?: ButtonVariant size?: ButtonSize loading?: boolean} export const Button = forwardRef<HTMLButtonElement, ButtonProps>( ( { variant = "primary", size = "md", loading = false, disabled, type = "button", className, children, ...props }, ref, ) => ( <button ref={ref} type={type} disabled={disabled || loading} aria-busy={loading || undefined} className={cn(buttonRecipe({ variant, size }), className)} {...props} > {loading && <Spinner aria-hidden="true" />} <span className={cn(loading && "opacity-70")}>{children}</span> </button> ),)Button.displayName = "Button"Inspect a difference
Accessibility
Why
A native <button> brings keyboard support, focus and the right role for free. type="button" avoids accidental form submits, aria-busy announces loading, and the label stays in place so screen readers still read it.
Trade-off
None worth avoiding here, since a styled <div> needs role, tabIndex and key handlers to get only part of this.
Where each tool fits.
Tools I use in this workflow. They are not connected to this page, they are a map of how I work.
Tools
- Figma
- Figma variables
Activities
- User flows and component design
- Variables for colour, spacing and radius
Deliverables
- Figma frames
- Token set
What I optimise for.
- P01
Reusable, maintainable components
One recipe drives every variant and size; the playground prints the same source it renders.
See the playground - P02
Accessible interfaces
Native elements, visible focus, labelled inputs and announced states, the first thing I check in review.
See the accessibility diff - P03
Consistent design systems
Components read semantic tokens, so theme, radius and accent change through configuration, not edits.
Change the tokens - P04
Type safety and predictable state
Unions over boolean flags; a single config object is the only state the playground has.
See the API diff - P05
Performance and responsive behaviour
This page ships no highlighting library and calls no API; the code view is a small local tokenizer.
See the process - P06
Human review of AI-generated code
AI drafts, I review: keyboard, screen reader, API ergonomics and visual QA before anything ships.
See step 3 - P07
Fast iteration, no unnecessary complexity
Small diffs, no new dependencies unless they pay for themselves, abstractions only when there is a second use.
See the workflow
Want this for your team?
If your team rewrites the same components in every app, or your agents keep inventing styles, I can set up the tokens, specs, components and registry for your product.