-
Notifications
You must be signed in to change notification settings - Fork 0
AI Agents
🔄 Auto-generated mirror. Canonical source:
docs/ai-agents.md. Edit there — changes here are overwritten by CI.
Nav: Quick start · Writing cases · Hierarchy · Tweaks · Theming · Documentation panel · Writing placard docs · Testing · CLI · AI agents · Configuration
Display Case is built to be read by machines as easily as by people. Two facts make it agent-friendly:
- A single manifest enumerates every component, case, doc, and tweak — as file references, not inlined content.
- Every case has a deterministic render URL that produces exactly one variant in isolation.
Together these let an agent discover what exists, read the source it points to, and snapshot any single state reproducibly.
Display Case requires Bun. Invoke it with bunx @awarebydefault/display-case <pkgDir> (or wrap it in a bun run display-case npm script — see CLI).
-
Enumerate without a browser or server:
bunx @awarebydefault/display-case <pkgDir> --print-manifestprints the manifest JSON to stdout and exits. This is the cheapest way to discover what exists. -
Start the server (needed for
/render,/manifest.json,/doc):bunx @awarebydefault/display-case <pkgDir>serves athttp://localhost:3100(override with--port). It needs no database or app — only the showcased package. It runs until killed. -
Accessibility result for a variant (only when
a11y.enabled):GET /a11y?component=<id>&case=<id>&theme=light|darkreturns{ status: 'ok', violations: [{ id, help, nodes, impact, details? }] }when cached,{ status: 'pending' }when a scan was just enqueued (poll again, or it's pushed over the SSE stream as ana11yevent), or{ status: 'unavailable', reason }when the scan toolchain can't run. The headless CI form isdisplay-case check --a11y(exits non-zero on any violation). -
Read the full a11y findings without re-running:
display-case check --a11yprints each violation's affected nodes and writes the complete run to.display-case/a11y/last-check.json—{ scannedAt, total, results: [{ component, case, theme, violations }] }, where each violation carriesdetails: [{ target, html, failureSummary?, contrast?: { foreground, background, ratio, required, fontSize?, fontWeight? } }]. For a colour-contrast finding this is the exact element and failing pair; read this file to fix violations without driving a browser yourself. -
Rasterize a render:
/render/...returns a complete HTML document, not an image. To capture it, drive a headless browser with Playwright:const page = await browser.newPage() await page.goto('http://localhost:3100/render/tweak-control/playground?theme=dark') await page.evaluate(() => document.fonts.ready) await page.screenshot({ path: 'tweak-control-dark.png' })
/manifest.json (or --print-manifest) returns a lightweight index. It contains paths to the real artifacts, so an agent reads the manifest first, then opens only the files it needs.
Per component you get its level, isFlow, the package-relative caseFile, and the package-relative placardDoc (or null). Per case you get its browseUrl, its renderUrl, the declared tweaks schema (or null), and its transitions — the slugified ids of steps it can advance to (a flow's steps; [] for a regular case).
renderUrl points at the isolated render document — the same one the browse iframe embeds and the check runner screenshots. It accepts query parameters so an agent can pin an exact state:
/render/tweak-control/playground?theme=dark&width=480&t.label=Variant&t.kind=choice&t.disabled=1
| Parameter | Meaning |
|---|---|
theme=light|dark |
Sets data-theme on the document root. Anything but dark is light. |
width=<px> |
Constrains the render to a centered max-width container. |
t.<name>=<value> |
Sets a tweak. boolean → 1/true; number → numeric; text/choice verbatim. Missing values fall back to defaults. |
Because the state lives entirely in the URL, the same URL always produces the same render — ideal for deterministic screenshots and diffs. See Tweaks and Theming for the encoding rules.
If placardDoc is non-null, the raw Markdown is served verbatim at /doc/<component> (content type text/markdown). An agent can fetch it directly for usage guidance, or just read the placardDoc path from the filesystem. See Documentation panel.
To write a placardDoc rather than read one, follow Writing placard docs — the bundled display-case-author-placard-doc skill drives exactly that workflow.
The dev server exposes:
| Path | Returns |
|---|---|
/ and /c/<component>/<case>
|
The browsing shell (client-side routed). |
/render/<component>/<case> |
The isolated render document (accepts theme, width, t.*). |
/manifest.json |
The manifest described above. |
/doc/<component> |
The raw .placard.md for that component (404 if none). |
/health |
ok. |
Cases render in an isolated browser bundle, which shapes a few behaviors worth knowing when a render looks wrong:
-
Public env is inlined. The bundle inlines the consumer package's
BUN_PUBLIC_*env (from<pkg>/.env[.local]) viaBun.build'sdefine, mirroring the app's own build. So app code that readsprocess.env.BUN_PUBLIC_*(e.g. an API base URL) gets a value instead of throwingprocess is not definedand blanking the whole single-bundle showcase. Non-public env is not inlined — a bundled module that readsprocess.cwd(),process.env.NODE_ENV,Bun.*, etc. still throws. - Runtime-global guard. If a bundled module does reference an undefined Node/Bun global at load, the render document catches it and paints an explanatory banner (instead of a silent blank).
-
No frame navigation. Anchor clicks that would unload the isolated frame are neutralized (a case has no router); same-document
#hashlinks andtarget=_blankare left alone.
display-case init [pkgDir] [--agent=claude] [--with-visual] [--dry-run] [--json] wires Display Case into a repo: it merges a launch entry into the agent's launch config, installs the bundled skills, and adds an agent-guide pointer — idempotently. A re-init is self-healing: skills, the launch entry, and the sentinel-delimited pointer block are each reconciled against the bundled content, so a drifted artifact is refreshed (reported as updated) rather than skipped. display-case uninstall reverses exactly those, removing only Display Case's own artifacts. Both default to a human-readable report; pass --json to get a machine-parsable plan/report ({ command, agent, dryRun, items: [{ artifact, action, detail }] }) and --dry-run to preview without writing.
--with-visual additionally sets up the optional visual-regression toolchain — it runs bun add --dev playwright @axe-core/playwright pixelmatch pngjs then bunx playwright install chromium. Without the flag the step is skipped (reported as a skipped plan item); when run interactively in a TTY, init prompts for it. You need this only for a default-backed check — browsing, --print-manifest, and /render snapshotting need no browser or visual deps. See Testing → the default backend is lazy and optional.
-
Enumerate. Run
bunx @awarebydefault/display-case <pkgDir> --print-manifest(no server needed), orGET /manifest.jsonagainst a running server. -
Locate. From the manifest, pick the
caseFile/placardDocpaths to read for source and guidance, and note each case'srenderUrlandtweaks. -
Snapshot. Hit
renderUrlwith the desiredtheme,width, andt.*parameters to render exactly the variant you want — then screenshot or scrape it. -
Document. When a component has no
placardDoc(or a thin one), author<name>.placard.mdper Writing placard docs so the next agent can use it without reading the source. -
Verify. Use
bunx @awarebydefault/display-case check <pkgDir>to confirm the structure best-practice rules, a11y, and visual baselines still pass after a change;bunx @awarebydefault/display-case check <pkgDir> --structure --tokensruns just the fast, browser-free phases (see Testing).
Auto-generated mirror of the Display Case docs. Edit the repo, not the wiki.
Product documentation
- AI Agents
- CLI
- Configuration
- Documentation Panel
- Examples
- Hierarchy
- Quick Start
- Style Engines
- Testing
- Theming
- Tweaks
- Writing Cases
- Writing Placard Docs
Contributing (engineering)
{ "title": "Display Case", "components": [ { "id": "tweak-control", "name": "TweakControl", "level": "atom", "isFlow": false, "caseFile": "src/components/tweak-control.case.tsx", "placardDoc": "src/components/tweak-control.placard.md", "cases": [ { "id": "variants", "name": "Variants", "browseUrl": "/c/tweak-control/variants", "renderUrl": "/render/tweak-control/variants", "tweaks": null, "transitions": [] }, { "id": "playground", "name": "Playground", "browseUrl": "/c/tweak-control/playground", "renderUrl": "/render/tweak-control/playground", "tweaks": { "label": { "kind": "text", "default": "Variant" }, "kind": { "kind": "choice", "options": ["text", "boolean", "number", "choice"], "default": "choice" }, "disabled": { "kind": "boolean", "default": false } }, "transitions": [] } ] } ] }