Component docs that write themselves

Turn your saved registry, a code scan, and your token map into one MDX page per component plus a machine-readable llms.txt — generated, gap-honest, and reviewable in a pull request.

What you’ll learn: ds-docs

What you’ll learn

Component documentation is the chore everyone agrees matters and nobody keeps current. The props drift, a variant gets added, the Figma description never makes it across — and the docs quietly rot. ds-docs takes a different tack: it generates the docs from the things you already maintain. It reads the component registry you built earlier, scans your code for real props and variants, folds in your design tokens, and writes one MDX page per component — plus a single machine-readable llms.txt that describes the whole system for an AI assistant.

A note for designers and managers: you don’t write any of these pages by hand. ds-bridge assembles them from facts it can verify, and where a fact is missing — say, a Figma component with no authored description — it doesn’t paper over the hole. It labels it a gap and tells you exactly what’s absent. Honest docs beat pretty-but-wrong docs, every time.

Setup

You’ll need the ds-bridge plugin (Getting started covers install and setup) and one thing more: a saved registry. ds-docs reads .ds-bridge/registry.json, the Figma↔code map produced by ds-bridge registry build. If you haven’t built one yet, the parity-audit tutorial walks through it end to end — that’s the prerequisite for everything on this page.

# Generate docs from the saved registry, code scan, and tokens.
ds-bridge docs .

Steps

  1. Run it against your project. ds-docs does its work offline: it reads the saved registry, scans your code with ts-morph for props and variants, and discovers your token source. Here’s a real run against a small project — a registry with one matched Button, a code-only HeroPanel, and a Figma-only Tooltip:

    3 pages written to /your-project/.ds-bridge/docs
    
    ┌───────────┬──────────────────────────────────────────────┐
    │ component │ gaps                                         │
    ├───────────┼──────────────────────────────────────────────┤
    │ Button    │ missing-figma-description                    │
    │ HeroPanel │ unmatched-in-figma                           │
    │ Tooltip   │ unmatched-in-code, missing-figma-description │
    └───────────┴──────────────────────────────────────────────┘
    
    llms.txt: /your-project/.ds-bridge/docs/llms.txt · 4 gaps total
    

    Three components in, three pages out — nothing is dropped. Notice the gaps column: each component carries its own honest list of what couldn’t be resolved. The run still exits 0 — gaps are findings to act on, not failures.

  2. Read the three typed gaps. Each one names a specific missing fact, and each is purely informational:

    • missing-figma-description — the component is matched, but the Figma side has no authored description. (Descriptions need a live Figma fetch, so an offline run surfaces this for every matched component.) Action: write a description in Figma.
    • unmatched-in-code — Figma publishes this component, but the registry has no code match for it. Tooltip is figma-only. Action: build the component, or check why it didn’t match in parity-audit.
    • unmatched-in-figma — the codebase exports it, but Figma doesn’t publish it. HeroPanel is code-only. Action: publish the Figma component, or retire the code.
  3. Open a generated page. Each component gets a self-contained MDX file. Here is the real Button.mdx — props and variants come straight from the code scan, so they can’t drift from the source:

    ---
    title: Button
    status: gaps
    ---
    
    # Button
    
    ```tsx
    import { Button } from "components/button.tsx";
    ```
    
    ## Props
    
    | Prop | Type | Required |
    | --- | --- | --- |
    | `variant` | `"primary" \| "secondary"` | yes |
    | `disabled` | `boolean` | no |
    | `children` | `unknown` | yes |
    
    ## Variants
    
    - **variant**: `primary`, `secondary`
    
    ## Figma
    
    _No Figma description authored._
    
    Node: `10:1`
    
    > [!WARNING]
    > This component has documentation gaps:
    > - The matched Figma component has no authored description.
    

    The page is honest about its own gaps: the status: gaps frontmatter and the warning callout both say, in plain language, that the Figma description is missing. Fill it in upstream and the next run clears the warning.

  4. Read the machine-readable llms.txt. Alongside the human pages, ds-docs writes one llms.txt — a compact, plain-text description of the whole system meant for an AI assistant to load as context. It lists your tokens and every component with its real signature:

    # Design System
    
    > Machine-readable summary of the design system: tokens and components.
    
    ## Tokens
    
    Format: w3c
    Total: 0
    
    _No tokens._
    
    ## Components
    
    - Button(variant: "primary" | "secondary", disabled?: boolean, children: unknown) — components/button.tsx [gaps: missing-figma-description]
    - HeroPanel(title: string) — components/hero-panel.tsx [gaps: unmatched-in-figma]
    - Tooltip() — figma:10:9 [gaps: unmatched-in-code, missing-figma-description]
    

    (This sample project has no token file, so the token section reports Total: 0. Point ds-bridge at a real tokens.json and the same section fills in.) Hand this file to an assistant and it knows your exact component contracts — so the code it suggests stays on-system, just like in figma-impl.

  5. Document one component, or ask for machine output. Two flags cover the common variations. Filter to a single component by name:

    ds-bridge docs Button . --out filtered
    
    1 pages written to /your-project/filtered
    
    ┌───────────┬───────────────────────────┐
    │ component │ gaps                      │
    ├───────────┼───────────────────────────┤
    │ Button    │ missing-figma-description │
    └───────────┴───────────────────────────┘
    
    llms.txt: /your-project/filtered/llms.txt · 1 gaps total
    

    Or ask for --format json when a script or CI step needs to parse the result instead of read it:

    ds-bridge docs . --out site/docs --format json
    
    {
      "outDir": "/your-project/site/docs",
      "llmsPath": "/your-project/site/docs/llms.txt",
      "pages": [
        {
          "component": "Button",
          "path": "/your-project/site/docs/Button.mdx",
          "gaps": ["missing-figma-description"]
        },
        {
          "component": "HeroPanel",
          "path": "/your-project/site/docs/HeroPanel.mdx",
          "gaps": ["unmatched-in-figma"]
        },
        {
          "component": "Tooltip",
          "path": "/your-project/site/docs/Tooltip.mdx",
          "gaps": ["unmatched-in-code", "missing-figma-description"]
        }
      ]
    }
    

    --out controls where everything lands; it defaults to .ds-bridge/docs.

Result

You now have a docs folder that generates itself — one MDX page per component with real props and variants, plus an llms.txt your AI assistant can read — and a per-component gap list that tells you precisely what’s still missing. Because the output lands under .ds-bridge/docs/, it’s committed and PR-reviewable: your reviewers see the docs change in the same diff as the code change that caused it.

A word on exit codes, since they matter in CI. ds-docs exits 0 whenever it generates docs, even with gaps — gaps are work surfaced, not a broken build. It exits 2 only on an operational error: a missing registry, a path that isn’t a directory, an unknown --format, or a [component] filter that matches nothing. So in a pipeline, a nonzero exit means “something is wrong with the inputs,” never “your docs have gaps.”

When you’re ready to publish, the /ds-bridge:ds-docs slash command offers to copy the generated pages from .ds-bridge/docs/ into your real documentation directory — but only after you explicitly confirm. It won’t touch your docs folder on its own. Browse the whole set on the tutorials index.

Troubleshooting