Files
whynot-design/CONSUMING.md
tegwick 2de30beb7b
Some checks failed
ci / check (push) Has been cancelled
ci / release (push) Has been cancelled
feat(consumer): versioned IR manifest + drift-check (WHYNOT-WP-0003 T03-T07,T09)
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>
2026-06-27 19:35:45 +02:00

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:

  1. Pin a version — the package + your lockfile.
  2. Inspect it — ir/INDEX.md (browsable) + ir/manifest.json (machine).
  3. Get a grip on changesnpx @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, a tokensHash, and a content hash per 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. drift reads 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.