Files
whynot-design/workplans/WHYNOT-WP-0002-designbook-stack-adapters.md
tegwick 4e68a5c51d
Some checks failed
ci / check (push) Has been cancelled
ci / release (push) Has been cancelled
Record state-hub workstream + task IDs for WHYNOT-WP-0002
Written back by fix-consistency after registering the workstream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:51:10 +02:00

282 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: WHYNOT-WP-0002
type: workplan
title: "Technology-neutral designbook with stack adapters (Lit reference)"
domain: infotech
repo: whynot-design
status: proposed
owner: claude
topic_slug: custodian
created: "2026-06-22"
updated: "2026-06-22"
state_hub_workstream_id: "0a3511c1-1771-438b-9364-104d8f0de2f8"
---
# Technology-neutral designbook with stack adapters (Lit reference)
## Problem
Claude Design's designbook is **React-bound** (its `/design-sync` converter generates
React components and React-rendered previews; a non-React DS "has nothing for the design
agent to build with"). That defeats the idea of a designbook as a *technology-neutral UI
blueprint*. coulomb needs to stay open to different UI stacks (Lit today, others later)
while still using one Claude designbook for a consistent appearance.
## Approach
Keep React as the **interaction surface** with Claude Design (use the recommended React
designbook — it is what the tool supports), but introduce a **technology-neutral
intermediate representation (IR)** as the actual blueprint, and **per-stack adapters** that
project the IR onto each stack. Lit is the first/reference adapter. Adapters are
**scaffold + drift-detect**, not full behavioral codegen: tokens are fully generated,
new components get stubs, changed components get a drift report (never an overwrite), and
visual parity is checked against the designbook's own exemplars. Behavior stays
hand-authored per stack.
```
Claude Design (React designbook — canonical authoring surface)
│ /design-sync [exists]
designbook/ (local React mirror: components/<group>/<Name>/{.d.ts,.prompt.md,.html},
│ tokens/, styles.css, _ds_bundle.js, _ds_manifest.json)
│ make ir → ir/extract (the pivot)
ir/ (technology-neutral blueprint, committed + diffable)
│ tokens.json (W3C Design Tokens format)
│ components/<Name>.json (contract: props/attrs, variants, slots, events, docs, exemplar)
│ exemplars/<Name>.{png,html} (reference render from the designbook preview)
│ make adapt-lit → adapters/lit
adapters/lit/ (reference) tokens → CSS custom props (full gen)
new component → <wn-*> stub + contract scaffold
changed component → DRIFT REPORT (no overwrite)
make parity-lit → visual + contract parity vs exemplar
```
**Directionality is one-way: React → IR → stacks.** Lit-side changes do **not** flow back
automatically; a change to the shared language must be made in Claude Design (React) and
re-propagated. The Lit components remain hand-authored; the adapter keeps their *contract
and appearance* aligned, it does not own their behavior.
## Builds on (already in this repo)
- `designbook/` — local mirror of the Claude Design project + `designbook/README.md`.
- `make designbook-sync` / `RecentChanges.md` — record what a sync changed.
- `make designbook-check` + `scripts/check_designbook_staleness.py` — llm-connect backend
that detects when the cloud designbook moved ahead (the refresh trigger).
- `tokens/*.json`, `src/styles/*.css`, `src/elements/*.js` — the existing Lit DS, which
becomes the first adapter's target/baseline.
- Playwright visual harness (`tests/visual/`) — extended for parity in Phase 4.
---
## Phase 0 — Contracts & governance
Establish the durable interfaces before any code, so future stacks slot in cleanly.
## Define the IR schema
```task
id: WHYNOT-WP-0002-T01
status: todo
priority: high
state_hub_task_id: "66187d76-3755-4204-ad71-d9fae8ed38ac"
```
Specify the technology-neutral blueprint. Tokens: adopt the **W3C Design Tokens Community
Group** JSON format (`$value`/`$type`) so the token layer is a standard, not a bespoke
shape — `tokens/*.json` already holds the values. Component contract (`ir/components/<Name>.json`):
name, group, description, props (name/type/enum/default/required), **prop→attribute mapping**
(React camelCase prop → Lit attribute/property), slots, events, variant axes, `docsRef`,
`exemplarRef`. Write `ir/SCHEMA.md` + a JSON Schema for validation. Decide and document that
`ir/` is committed (diffable blueprint).
## Define the adapter interface
```task
id: WHYNOT-WP-0002-T02
status: todo
priority: high
state_hub_task_id: "6fe8481c-02b3-407d-9b7b-c47f161c0dcd"
```
Document the contract every stack adapter implements so Vue/Angular/plain-CSS adapters are
drop-in: inputs (`ir/`), outputs (generated artifacts into the stack's source tree, a
machine-readable **drift report**, a **parity result**), idempotency rules (regenerate tokens
+ stubs; never overwrite hand-authored behavior), and exit codes for CI. Write
`adapters/ADAPTER_CONTRACT.md`.
## Governance & propagation doc
```task
id: WHYNOT-WP-0002-T03
status: todo
priority: medium
state_hub_task_id: "97aadf8a-4d56-47d0-b841-d664d0676a53"
```
Document directionality (React canonical, one-way to stacks, Lit changes round-trip through
Claude Design), the drift-resolution workflow (who fills behavior, how a drift report is
triaged/closed), and how this extends the existing atelier→repo pipeline. Update
`DesignSystemIntroduction.md` (§5 propagation) and add a rule under `.claude/rules/`.
---
## Phase 1 — Canonical React designbook source
## Establish the React designbook as canonical
```task
id: WHYNOT-WP-0002-T04
status: todo
priority: high
state_hub_task_id: "ed4dd1d4-f649-40f0-83f5-9cbd88622a7b"
```
The pivot needs a real React source in Claude Design (the current project holds the
hand-authored web-component experiment, not a React component bundle). Decide how the
canonical React designbook is established and maintained: author a React expression of the
`wn-*` component set in Claude Design (native, `/design-sync`-compatible), tokens-first then
grow components, or adopt an existing React kit. Define how it is pulled into `designbook/`.
**Risk/dependency**: this is the precondition for IR extraction; scope it deliberately —
a minimal token-plus-core-components React designbook is enough to prove the pipeline.
---
## Phase 2 — IR extraction (the pivot)
## Build the IR extractor
```task
id: WHYNOT-WP-0002-T05
status: todo
priority: high
state_hub_task_id: "dcdc0b01-756f-4253-9599-e5d5dfbe1083"
```
Build `scripts/ir-extract.mjs`: read the `designbook/` React mirror — `.d.ts` (props),
`.prompt.md` (docs), `_ds_manifest.json` (groups), `tokens/` (values), preview `.html`
(exemplar render) — and emit `ir/tokens.json` (W3C format), `ir/components/<Name>.json`
(per T01 schema), and `ir/exemplars/<Name>.*`. Validate output against the JSON Schema from
T01. Add `make ir`. `ir/` is committed so a re-extract surfaces blueprint changes as a git diff.
---
## Phase 3 — Lit reference adapter
## Token generation (full gen)
```task
id: WHYNOT-WP-0002-T06
status: todo
priority: high
state_hub_task_id: "a106c673-e849-4d06-91c9-3f7f63fec2ea"
```
`adapters/lit/`: generate CSS custom properties from `ir/tokens.json` into
`src/styles/colors_and_type.css` (the existing token layer). Fully generated + deterministic;
re-running is a no-op when nothing changed. Add `make adapt-lit` (tokens portion first).
## Component scaffold + drift report
```task
id: WHYNOT-WP-0002-T07
status: todo
priority: high
state_hub_task_id: "00ed1aff-7724-4e90-9a51-fd58699480ca"
```
Extend `adapters/lit/`: for an IR component with no `wn-*` counterpart, generate a Lit stub
(`<wn-*>` skeleton + reactive properties from the contract's prop→attribute map + a TODO for
behavior). For an existing component, **emit a drift report** (`adapters/lit/drift/<Name>.md`):
prop/attribute mismatches, missing/extra variants, removed props — **never overwrite the
hand-authored element**. Map React prop types → Lit property declarations. Wire into
`make adapt-lit`.
---
## Phase 4 — Parity verification
## Contract + visual parity
```task
id: WHYNOT-WP-0002-T08
status: todo
priority: medium
state_hub_task_id: "1f52ca1f-64a6-4643-992f-f0b4812461a0"
```
`make parity-lit`: (a) **contract parity** — assert each `wn-*` element's observed
attributes/properties match its IR contract (fail on drift); (b) **visual parity** — render
the Lit component and diff against `ir/exemplars/<Name>` using the existing Playwright harness,
emit a parity diff. Produce a single parity result per the adapter contract (T02). This is the
gate that confirms Lit actually matches the designbook appearance.
---
## Phase 5 — Keep-up-to-date instruction set
## Refresh loop + runbook
```task
id: WHYNOT-WP-0002-T09
status: todo
priority: high
state_hub_task_id: "07e60a34-0c62-4f8b-848b-d3b8d4292a18"
```
Wire and document the end-to-end refresh sequence so keeping Lit current against the React
designbook is one routine:
1. `make designbook-check` — detect the cloud designbook moved (existing llm-connect backend).
2. `/design-sync` — pull the latest React designbook into `designbook/`.
3. `make designbook-sync` — record the diff (`RecentChanges.md`).
4. `make ir` — re-extract the IR; review the `ir/` git diff (the blueprint change).
5. `make adapt-lit` — regenerate tokens, scaffold new components, emit drift reports.
6. Resolve drift — fill/adjust Lit behavior guided by `adapters/lit/drift/*.md`.
7. `make parity-lit` — confirm appearance + contract parity.
Add a `make designbook-refresh` orchestrator for the automatable steps (15, 7) and a
human-in-loop runbook for step 6. Document in `.claude/rules/stack-and-commands.md` and
`designbook/README.md`.
---
## Phase 6 — Generalization hook (prove extractability)
## Second-adapter smoke (validate the boundary)
```task
id: WHYNOT-WP-0002-T10
status: todo
priority: low
state_hub_task_id: "483de131-f580-4031-85df-72cf70a45679"
```
Sketch a minimal second adapter (plain-CSS utility classes or a Vue stub) that consumes the
same `ir/` and implements the T02 contract — just enough to prove the IR/adapter boundary
holds and the architecture can be lifted into a coulomb-level tool later. Do not finish the
second stack; the deliverable is confidence in the seam.
---
## Open questions / risks
- **React designbook bootstrap (T04)** is the critical-path dependency — without a real React
source, the IR has nothing to extract. Keep its initial scope minimal.
- **Prop→attribute fidelity**: React props (objects, render props, callbacks) don't all map to
Lit attributes; the contract must mark non-portable props and the adapter must surface them
as drift, not silently drop them.
- **Exemplar parity tolerance**: web-component vs React rendering will differ at the pixel
level; parity needs a sensible diff threshold (reuse `maxDiffPixelRatio` discipline).
- **One-way constraint**: ensure the tooling never tempts a Lit→React back-edit that bypasses
Claude Design; governance doc (T03) must make the round-trip explicit.
## Registering this workplan
After review, register the workstream from `~/state-hub`:
```bash
make fix-consistency REPO=whynot-design
```