Design system & AI-native workflow
A token-first design system I built and ship to production, and the way I actually work with AI day to day — not generatively, but as tooling around real decisions.
@amezquita/design-system — published to npm, powering this site, built and maintained solo. 28 components, 620 design tokens, 184 Storybook/Chromatic stories, zero visual regressions across every build.
Token-first: every color, spacing, and type value traces back to a DTCG token, so the system stays consistent whether it's consumed by this site, another product, or an AI coding agent.
Past component libraries, I pushed this one to be AI-ready: an llms.txt and per-component machine-readable docs, a component registry an AI coding tool can pull directly from (npx shadcn add), a fabrication linter that catches an agent inventing a token that doesn't exist, and an agent skill it can read to understand the system's conventions before touching code.
Measured against public benchmarks for this: went from 1/5 to 3/5 on the DesignSystems.one signals, and from 1/10 to 5/10 on the Kaelig AI-readiness affordances — verified live on this site.
Below is Button — the most-referenced component in the system — shown both ways: the rendered component, and the actual generated doc twin an agent reads before it touches the code.
The rendered component
The generated doc twin — docs/components/button.md
# Button
> Trigger for user actions; renders as `<button>` or `<a>` depending on context
- Tier: primitives
- Storybook: `Components/Button`
- Import: `import { Button } from '@amezquita/design-system/components/primitives/Button'`
## Props
| Prop | Type | Description |
|---|---|---|
| `variant?` | `'primary' \| 'secondary' \| 'ghost' \| 'link'` | |
| `children` | `React.ReactNode` | |
| `onClick?` | `() => void` | |
| `disabled?` | `boolean` | |
| `loading?` | `boolean` | |
| `fullWidth?` | `boolean` | |
| `type?` | `'button' \| 'submit' \| 'reset'` | |
| `icon?` | `React.ReactNode` | |
| `iconPosition?` | `'start' \| 'end'` | |
| `noArrow?` | `boolean` | |
| `aria-label?` | `string` | |
| `href?` | `string` | |
| `curtainColor?` | `string` | |
| `onNavigate?` | `(href: string, curtainColor?: string) => void` | Called instead of a plain navigation when set and the link is internal — lets host apps inject route-transition behavior (e.g. a page-curtain animation) without Button depending on any specific router or transition system. Omit for a plain internal navigation. |
## Tokens
| Token | Type | Value |
|---|---|---|
| `--button-arrow-nudge` | dimension | `2px` |
| `--button-border-radius` | dimension | `9999px` † |
| `--button-border-width` | dimension | `1px` |
| `--button-duration` | duration | `200ms` |
| `--button-font-size` | dimension | `16px` |
| `--button-font-weight` | fontWeight | `500` |
| `--button-ghost-background` | color | `transparent` |
| `--button-ghost-background-hover` | color | `#F4F0EB` † |
| `--button-ghost-border` | color | `transparent` |
| `--button-ghost-foreground` | color | `#0A0A0A` † |
| `--button-glow-size` | dimension | `52px` |
| `--button-icon-gap` | dimension | `8px` |
| `--button-icon-size` | dimension | `20px` |
| `--button-outline-border-width` | dimension | `2px` |
| `--button-padding-x` | dimension | `24px` |
| `--button-padding-y` | dimension | `12px` |
| `--button-primary-background` | color | `#292524` † |
| `--button-primary-background-hover` | color | `#1C1917` † |
| `--button-primary-border` | color | `#292524` † |
| `--button-primary-foreground` | color | `#FFFFFF` † |
| `--button-secondary-background` | color | `transparent` |
| `--button-secondary-background-hover` | color | `#292524` † |
| `--button-secondary-border` | color | `#292524` † |
| `--button-secondary-foreground` | color | `#292524` † |
| `--button-secondary-foreground-hover` | color | `#FFFFFF` † |
| `--button-spinner-duration` | duration | `750ms` |
| `--button-wipe-duration-curve` | duration | `220ms` |
| `--button-wipe-duration-curve-out` | duration | `180ms` |
| `--button-wipe-duration-enter` | duration | `550ms` |
| `--button-wipe-duration-exit` | duration | `400ms` |
† resolves differently across light/dark and default/bold themes — see `tokens.json` for all four values.
## Accessibility
- Semantic element: `<button>` by default; `<a>` when `href` is passed
- When rendered as `<a>`: no `disabled` attribute — use visual suppression only if truly needed
- ARIA: use `aria-label` for icon-only buttons; `disabled` on `<button>` removes it from tab order
- Keyboard: `Enter` + `Space` activate `<button>`; `Enter` follows `<a>`I use Claude Code as a daily tool, not a novelty. The core habit is writing down the why, not just the what — decisions get recorded as short ADRs (architecture decision records) the moment they're made, so the reasoning survives past the session that produced it.
That habit became its own open-source starting point: a template repo structured around three layers — memory (what stays stable: product direction, brand, architecture, quality bar), methods (repeatable skills for how work gets done), and logs(specs for what's being built now, decision records for why past calls went the way they did). An agent enters through one file and reads the same source of truth a person would.