Make ir/ the unit of versioned downstream consumption so consuming repos can pin a version, inspect it, and follow changes at their own pace. - T03 ir/manifest.json: per-version inventory + diff anchor with deterministic sha256-over-canonicalised-JSON hashes; no-churn generatedAt; manifest schema. - T07 ir/INDEX.md: human-readable catalog generated by make ir. - T04 .whynot-design.lock sync-point format + lock schema. - T05 npx @whynot/design drift: consumer drift-check (bin entry), exit 0/2/3, --json/--update/--manifest/--version/--lock. - T06 CONSUMING.md guide + examples/consumer-fixture/ runnable demo; README + MultiFrameworkSupport cross-links; fix README version pin (@0.3.0 not @v0.3.0). - T09 CONSUMER_CONTRACT_PARITY.md design-only note (live-UI parity deferred). T02 (publish) and T08 (showcase, blocked on WP-0002 T11) remain wait. Repo stays in dev mode; no outward publish performed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.0 KiB
Consuming whynot-design from another repo
whynot-design is the upstream visual reference for other repos. It is a development reference and demo platform — it does not run as a production workload and handles no critical data. Consuming repos build their production UIs from it, and follow up on changes at their own pace — they are never force-synced.
A consumer tracks the IR (ir/), not the Lit internals. The IR is the
technology-neutral contract: per-component contracts (ir/components/*.json),
W3C-DTCG tokens (ir/tokens.json), exemplars, and the version anchor
ir/manifest.json. Three moves make this work:
- Pin a version — the package + your lockfile.
- Inspect it —
ir/INDEX.md(browsable) +ir/manifest.json(machine). - Get a grip on changes —
npx @whynot/design drift.
This is the inverse of whynot-design's own upstream machinery
(Claude Design → designbook/ → ir/), now pointed downstream
(ir/ → your repo). It is one-way: you read the IR; you never write back.
1. Pin a version
@whynot/design is published to the coulomb Gitea npm registry. Pin an exact
tagged version; your lockfile becomes the real pin.
# .npmrc in your repo (see PUBLISHING.md for the read-token routing)
# @whynot:registry=https://gitea.coulomb.social/api/packages/coulomb/npm/
npm i @whynot/design@0.3.0 lit
lit is a peer dependency — install it alongside so your bundler dedupes to
a single lit instance.
2. Inspect what you pinned
No clone, no build needed:
node_modules/@whynot/design/ir/INDEX.md— human-readable catalog: every component, its tag, props/variants/slots/events, and a link to its exemplar.node_modules/@whynot/design/ir/manifest.json— the machine inventory:designVersion, atokensHash, and a contenthashper component.
3. Adopt a sync-point
Record which IR state your repo has reconciled against. Run once, in your repo root:
npx @whynot/design drift --update
This writes .whynot-design.lock — commit it. It is the consumer-side mirror
of whynot-design's own designbook/.design-sync.json.
.whynot-design.lock format
{
"designVersion": "0.3.0",
"adoptedAt": "2026-06-27T17:31:08.640Z",
"manifestSchemaVersion": "1.0.0",
"manifestHashes": {
"tokens": "sha256:426f565a9ce6c36f",
"components": {
"Button": "sha256:4a32713049e433dd",
"TopNav": "sha256:32ebc6e46db38f93"
}
}
}
| field | meaning |
|---|---|
designVersion |
the @whynot/design version you adopted |
adoptedAt |
when you adopted it (first adopt, or last drift --update) |
manifestSchemaVersion |
the manifest's shape version; a mismatch warns that hashes may not be directly comparable |
manifestHashes.tokens |
adopted value of the manifest tokensHash |
manifestHashes.components |
adopted content hash per component |
Lifecycle: created on first drift --update, advanced only by a later
drift --update. Nothing else writes it. Schema: ir/schema/lock.schema.json.
4. Follow up at your own pace
When you bump @whynot/design (or just want to know what moved), run:
npx @whynot/design drift
It compares your adopted .whynot-design.lock against the installed package's
ir/manifest.json and reports added / changed / removed components plus
whether tokens changed:
whynot-design drift
adopted: 0.3.0 (2026-06-27T17:31:08.640Z)
target: 0.4.0 (2026-07-10T09:02:11.400Z)
Tokens: changed
Components: +1 added · ~1 changed · -0 removed · 11 total
+ Banner
~ Button
Drift detected vs your adopted sync-point.
Adopt this version: npx @whynot/design drift --update
Then, when you are ready, review the changed contracts in ir/INDEX.md, update
your UI, and adopt the new sync-point:
npx @whynot/design drift --update
Exit codes (CI-friendly)
Mirrors the adapter contract (adapters/ADAPTER_CONTRACT.md):
| code | meaning |
|---|---|
0 |
in sync — your lock matches the target |
2 |
usage / config error (bad flag, missing/invalid manifest or lock) |
3 |
drift detected — something changed since your sync-point |
Add --json for automation. Useful flags: --manifest <path> (diff against an
explicit manifest, e.g. a fetched newer version on disk), --version <x.y.z>
(assert the resolved manifest is that version — guards against the wrong install),
--lock <path> (non-default lock location).
No network, no writes to the package.
driftreads only the already-installed package + your lock, and the only file it ever writes is your repo's.whynot-design.lock.
Try the full loop now
A copy-pasteable fixture lives at
examples/consumer-fixture/ — it exercises
pin → inspect → drift → update against a fixed version without needing a real
install. See its README.md.
See also: README.md Tracking whynot-design ·
MultiFrameworkSupport.md ·
ir/SCHEMA.md.