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>
151 lines
5.0 KiB
Markdown
151 lines
5.0 KiB
Markdown
# 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 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.
|
|
|
|
```bash
|
|
# .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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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/`](./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`](./README.md) *Tracking whynot-design* ·
|
|
[`MultiFrameworkSupport.md`](./MultiFrameworkSupport.md) ·
|
|
[`ir/SCHEMA.md`](./ir/SCHEMA.md).
|