# Design System Introduction > How `whynot-design` fits into the broader `whynot` workflow — from atelier exploration to production deploys, across React, Django, plain HTML, or any future consumer. > > Audience: anyone (human or agent) about to add `whynot-design` as a dependency, or contribute changes back to it. --- ## 1. Mental model — three places, three jobs `whynot-design` is one of three surfaces. Each has a different job. Don't confuse them. | Place | Job | What lives there | |---|---|---| | **Claude atelier project** (`WhyNot Design System` template) | Explore, decide, mock | HTML cards, prototypes of new components, the README that defines the rules. Source of truth for the *language* of the system. | | **`whynot-design` repo** (this one) | Distribute | A versioned, publishable package: tokens, CSS, web components, the logo bundle. Source of truth for the *artefact*. | | **`whynot-*` consuming repos** (apps, prototypes, marketing sites) | Use | Add `@whynot/design` as a dependency. Use it from React, Django, Vue, or plain HTML — see [`MultiFrameworkSupport.md`](./MultiFrameworkSupport.md). | This mirrors the existing organisational logic. `whynot-control` is the *control* surface (intent, scope, decisions, governance). `whynot-design` is the *implementation* surface for the visual language. > The Claude-Design template is the atelier for the *next* design exploration. It is **not** what production code consumes. Production consumes this repo. --- ## 2. The three-layer architecture `whynot-design` is **deliberately framework-agnostic**. It ships in three stacked layers, and consumers pick how deep they go. ``` ┌─────────────────────────────────────────────────────────────┐ │ Layer 3 — Framework adapters (optional) │ │ Django partials · React thin wrappers · Vue, Svelte, … │ │ All thin. All delegate to Layer 2. │ ├─────────────────────────────────────────────────────────────┤ │ Layer 2 — Web components (canonical) │ │ , , , … │ │ Lit-based. Light DOM. SSR-friendly. Work in any framework. │ ├─────────────────────────────────────────────────────────────┤ │ Layer 1 — Tokens + CSS │ │ colors_and_type.css · components.css · tokens/*.json │ │ Plain CSS variables and utility classes. Works in any HTML.│ └─────────────────────────────────────────────────────────────┘ ``` ### Why this shape - **Layer 1 is the bedrock.** Tokens are framework-agnostic by definition. `colors_and_type.css` is just CSS variables — Django, Rails, Phoenix, plain HTML, any framework can `` to it and inherit the system's typography and palette tomorrow. The component utility classes in `components.css` (e.g. `.wn-btn`, `.wn-card`) compose those tokens into the canonical recipes. **Anything in this repo can be consumed with no JS at all.** - **Layer 2 adds behaviour without lock-in.** Web Components are a stable platform feature — they're not a framework dependency that ages out. Lit is the implementation detail; the *contract* is the HTML custom-element API. A `` works in a React JSX file, a Django template, a Vue SFC, and `index.html` identically. **Layer 2 components render to light DOM**, which means Layer 1's CSS styles them — no shadow-DOM style isolation, no FOUC, no SSR friction. - **Layer 3 is convenience, not commitment.** If a consumer wants typed React props or Django `{% include %}` shorthand, the repo ships *thin* wrappers in `adapters/`. They delegate to Layer 2. Removing or refactoring them does not break Layer 2 consumers. ### Which layer should *you* use? | If your consumer is… | Use this layer | Example | |---|---|---| | Django (server-rendered HTML + HTMX) | Layer 1 + 2 directly, or Layer 3 partials | `{{ label }}` | | React (any meta-framework) | Layer 2 directly | `Promote` — React handles custom elements natively | | Plain HTML page / a prototype landing page | Layer 2 directly | `Sign up` | | Email, PDF, or no-JS context | Layer 1 only | `` | | You only need typography & colours | Layer 1 only | Just `` the CSS. | The discipline: **do not subclass, fork, or restyle Layer 2 components in your repo**. If a component doesn't fit a use case, add a variant *upstream* in `whynot-design`. The thing that makes a design system valuable is the discipline of not forking it locally. For the full how-to per framework — including SSR, hydration, the `noscript` story, form participation, and HTMX integration — see [`MultiFrameworkSupport.md`](./MultiFrameworkSupport.md). --- ## 3. What this repo contains ``` whynot-design/ ├── README.md Full design language. ├── DesignSystemIntroduction.md This file. ├── MultiFrameworkSupport.md How to consume from React, Django, Vue, plain HTML. ├── SKILL.md Agent Skill manifest, cross-compatible with Claude Code. ├── CONTRIBUTING.md How to propose, review, and ship a change. ├── CHANGELOG.md Hand-edited, one entry per release. ├── BOOTSTRAP.md First-push instructions. Delete after use. ├── package.json @whynot/design — Lit is the one runtime dependency. │ ├── tokens/ Source-of-truth design tokens (JSON). │ ├── src/ │ ├── styles/ │ │ ├── colors_and_type.css Layer 1 — tokens + semantic element styles. │ │ └── components.css Layer 1 — utility classes for all components. │ ├── elements/ Layer 2 — Lit web components, light DOM. │ │ ├── atoms.js button, tag, eyebrow, stamp, stage-dot, phase-dot, icon │ │ ├── form.js input, textarea, select, search-input, field-row │ │ ├── layout.js card, modal, table, toast, empty-state, breadcrumb │ │ └── chrome.js top-nav, sidebar, page-header, pipeline, prototype-card │ └── index.js Side-effect import: registers all custom elements. │ ├── adapters/ Layer 3 — optional wrappers. │ └── django/templates/whynot/ Django {% include %} partials over the web components. │ ├── assets/ Logo, mark, future imagery. ├── examples/ │ ├── showcase/ Plain-HTML demo of every component. │ └── whynot-control/ React + custom-element click-through UI kit. └── .gitea/workflows/ Lint + Playwright visual regression on PR. ``` --- ## 4. Integrating with a consuming codebase ### 4.1 Distribution channels at A1 In order of effort: **a) pnpm workspaces (recommended for now)** — put `whynot-design` and your consuming app in the same monorepo (or use `file:` / `link:` references). Zero registry, zero auth, instant updates. **b) Install directly from Gitea** — no registry needed. ```sh pnpm add git+ssh://git@gitea.example.com/whynot/whynot-design.git#v0.2.0 ``` Pin to a tag, not `main`. Tag-pinning is the entire versioning discipline at A1. When you outgrow either (second team needs read access without cloning, semver resolution becomes valuable), publish to **Gitea Packages** (native npm protocol) or a private Verdaccio. ### 4.2 What a consumer imports The smallest viable consumption is: ```html ``` That's it. Now ``, ``, `` etc. work everywhere on the page, in any framework, server-rendered or client-rendered. For a Node-tooled consumer: ```jsx // At app root, once. import "@whynot/design/styles/colors_and_type.css"; import "@whynot/design/styles/components.css"; import "@whynot/design"; // side-effect: registers all custom elements // In any component file — React, Vue, Svelte, all behave the same. function NewBetaPage() { return (
whynot · closed beta

Concierge prototype triage

Request an invite
); } ``` Three rules of consumption: 1. **Import the CSS once at the app's root.** Both stylesheets. 2. **Use tokens for any colour, type, or spacing decision** that components don't cover. If the token doesn't exist, that's a signal to extend the system upstream — not to invent. 3. **Don't restyle components by overriding their CSS.** If a component doesn't fit, contribute a variant. ### 4.3 Bootstrapping a new consuming repo ```sh mkdir whynot-prototype-WNO-022 cd whynot-prototype-WNO-022 pnpm init pnpm add @whynot/design # or the git+ssh URL # Then in your entry point: echo 'import "@whynot/design/styles/colors_and_type.css"' >> src/main.js echo 'import "@whynot/design/styles/components.css"' >> src/main.js echo 'import "@whynot/design"' >> src/main.js ``` If it takes longer than this, the design system is fighting you. --- ## 5. Propagation pipeline — five hops The end-to-end flow for a single design change: ``` [Claude atelier] [whynot-design] [Consuming repo] [Deploy] explore variants ──► PR with token / ──► Renovate / pnpm ──► staging in the template component change up opens PR bumping user approves + Playwright diff @whynot/design │ + CHANGELOG entry ▼ │ │ prod tag v0.3.1 CI runs visual CI publishes / regression on the attaches release consuming app asset │ │ merge if green └──────────────────────────────┘ ``` ### Hop-by-hop 1. **Atelier → `whynot-design` PR.** Someone (you, a designer, or an agent) takes a change agreed in the Claude project and opens a PR against `whynot-design`. The PR description quotes the atelier decision. 2. **`whynot-design` CI** runs: - Lint + (optional) typecheck. - **Visual regression** — Playwright screenshots `examples/showcase/index.html` and `examples/whynot-control/index.html`, diffs against baselines. This catches both styling regressions and behavioural ones. - A `CHANGELOG.md` entry is required. 3. **Merge → tag → publish.** On merge to `main`: - Bump `package.json` (patch for token tweaks, minor for new components, major for renames/removals). - Tag `v0.3.1`. - Push tag. CI uploads release notes from the CHANGELOG. 4. **Consumer auto-PR.** Renovate or Dependabot watches `@whynot/design` and opens a PR in every consuming repo bumping the version. 5. **Consumer CI + deploy.** The consuming repo's *own* CI runs *its* visual regression. If unchanged, auto-merge. If changed, a human reviews. Merge triggers existing deploys to staging → prod. The whole loop, warm, takes minutes. **Automation works only because every step has a deterministic check** — visual regression on both sides, semver, changelogs. Skip those and the pipeline is a slow manual process with extra tools. --- ## 6. Versioning discipline Strict semver, even at A1. | Change | Bump | |---|---| | Token value tweak that doesn't visibly break any existing example | **patch** — `0.2.0 → 0.2.1` | | New component, new variant, new token | **minor** — `0.2.0 → 0.3.0` | | Removing / renaming a component, prop, or token; changing default behaviour | **major** — `0.3.0 → 1.0.0` | Stay in `0.x.x` until something built with this system is in production. While in `0.x.x`, **minor bumps are allowed to break things** — that's the convention. This gives you permission to iterate without ceremony. Promotion past `1.0.0` should appear in `whynot-control/DECISIONS.md`. Same rule as promotion to Helix or Coulomb: it's a deliberate act, not a release-script side-effect. --- ## 7. Where Claude fits Two distinct roles, both useful: - **The Claude atelier template** — used at hop 1. Designer (or you) opens a new project, mocks variations, decides, hands off a PR description + diff to whoever writes the `whynot-design` PR. The atelier never publishes anything to production directly. - **Claude Code with `SKILL.md`** — used at hop 1 *and* hop 5. The same SKILL file works in both contexts: - Pointed at `whynot-design`, Claude Code can write component PRs. - Pointed at a consuming repo, Claude Code can build screens that respect the rules. That's why `SKILL.md` ships with this repo. Drop it into `.claude/skills/` of any consuming repo and any agent operating in that repo will know the visual language. --- ## 8. Pragmatic A1 staging — don't build the whole pipeline yet Right now, `whynot` is at A1 Incubating. Build the smallest pipeline that still has the right *shape*. Promote each piece only when a real signal demands it. | Hop | A1 version | Promote to full when… | |---|---|---| | Atelier | Claude template, as-is. | Never — stays here. | | `whynot-design` repo | This seed. Tokens + CSS + Lit web components. | Never — this is the canonical shape. | | Distribution | pnpm workspace, or `git+ssh` install from tags. | An external collaborator needs read access without cloning. | | Visual regression | Playwright over `examples/showcase/` and `examples/whynot-control/`. | The system has >40 components or >3 consuming apps. | | Dependency updates | Manual `pnpm up` once a week. | More than two consuming repos. | | Release notes | Hand-edited `CHANGELOG.md`. | More than two contributors. | | Per-framework adapters | Django partials only, for the 4–5 most-used components. | Another non-React, non-Django consumer appears. | This staging is exactly the *"low-cost learning first"* posture in `whynot-control/OPERATING_MODEL.md`. A design system with one consumer and one author does not need Chromatic. A design system with five consumers and three authors absolutely does. --- ## 9. First-week checklist For whoever is bootstrapping this repo right now: - [ ] Push the seed contents to `gitea.example.com/whynot/whynot-design`. - [ ] Tag `v0.2.0` immediately so consumers can pin. - [ ] Add the repo as a remote dependency in **one** consuming app (the Django one) and verify imports work end-to-end. Follow [`MultiFrameworkSupport.md` §Django](./MultiFrameworkSupport.md#django-server-rendered-templates--htmx). - [ ] Open one trivial PR against `whynot-design` (e.g. a CHANGELOG typo) to confirm CI passes end-to-end. - [ ] Record this bootstrap in `whynot-control/DECISIONS.md` as DEC-004 — *"Established whynot-design as the implementation surface, three-layer architecture, Lit web components as the canonical component layer."* - [ ] Update `whynot-control/SCOPE.md` to mention `whynot-design` as a sibling. That's it. Anything more is over-engineering for the current stage. --- > A design system can be interesting and still be parked. `whynot-design` exists to reduce visual uncertainty across prototypes, not to create more obligations.