tegwick 9b9f372893
Some checks are pending
ci / check (push) Waiting to run
ci / release (push) Blocked by required conditions
Add capability registry scaffold (REUSE-WP-0014-T08 B06)
2026-06-16 02:03:01 +02:00
2026-05-23 16:34:14 +02:00
2026-05-23 16:34:14 +02:00
2026-05-23 16:34:14 +02:00
2026-05-23 16:34:14 +02:00
2026-05-23 16:34:14 +02:00
2026-05-23 16:34:14 +02:00
2026-05-23 16:34:14 +02:00

@whynot/design

The neutral, mostly-black-and-white visual language for whynot — Tegwick's prototype and market-signal organisation. Wireframe-leaning. Quiet. Built for artefacts that should look deliberately unfinished.

A prototype is a question made tangible. — whynot-control/INTENT.md

This repository is the implementation surface for whynot's visual language. It ships:

  • Tokens (tokens/*.json) — source-of-truth colour, type, spacing values.
  • CSS (src/styles/*.css) — drop-in stylesheets for any HTML context.
  • Web components (src/elements/*.js) — Lit-based custom elements that work in React, Django, Vue, Svelte, plain HTML, anywhere.
  • Adapters (adapters/django/) — optional {% include %} partials for Django teams.

Framework-agnostic by design. Consumers do not re-implement components per framework — they use the same <wn-button> everywhere.

Read first

File What's in it
DesignSystemIntroduction.md How this repo relates to whynot-control, the Claude atelier, and consuming apps. Three-layer architecture, propagation pipeline, versioning, A1 staging.
MultiFrameworkSupport.md How to consume from React, Django, HTMX, Vue, plain HTML. Per-framework setup, SSR specifics, hydration story.
SKILL.md Agent Skill manifest, cross-compatible with Claude Code. Drop into .claude/skills/ of any consuming repo.
This README Full design language: tokens, components, content rules, iconography.
CONTRIBUTING.md How to propose, review, and ship a change.
BOOTSTRAP.md First-push instructions for priming this repo. Delete after use.

Quick start

Plain HTML

<link rel="stylesheet" href="/whynot/colors_and_type.css">
<link rel="stylesheet" href="/whynot/components.css">
<script type="module" src="/whynot/index.js"></script>

<wn-button variant="primary">Promote prototype</wn-button>

Node-tooled consumer (React, Vite, Next, Vue, …)

pnpm add git+ssh://git@gitea.example.com/whynot/whynot-design.git#v0.2.0
// once, at app root
import "@whynot/design/styles/colors_and_type.css";
import "@whynot/design/styles/components.css";
import "@whynot/design";
<wn-button variant="primary">Promote prototype</wn-button>
<wn-pipeline active-idx="3"></wn-pipeline>

Django

{# templates/base.html #}
{% include "whynot/_base_head.html" %}

{# any template #}
<wn-page-header eyebrow="whynot · prototypes" title="Prototypes"></wn-page-header>
<wn-prototype-card card-id="{{ p.id }}" signal="{{ p.signal }}">
  <span slot="pitch">{{ p.pitch }}</span>
</wn-prototype-card>

See MultiFrameworkSupport.md for the full integration story per framework.

What lives where

whynot-design/
├── 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 every component.
│   ├── elements/                     Layer 2 — Lit web components.
│   │   ├── atoms.js                  wn-button, wn-tag, wn-eyebrow, wn-stamp,
│   │   │                             wn-stage-dot, wn-phase-dot, wn-icon
│   │   ├── form.js                   wn-input, wn-textarea, wn-select,
│   │   │                             wn-search-input, wn-field-row
│   │   ├── layout.js                 wn-card, wn-modal, wn-table, wn-banner,
│   │   │                             wn-toast, wn-empty-state, wn-breadcrumb
│   │   ├── chrome.js                 wn-top-nav, wn-sidebar, wn-page-header,
│   │   │                             wn-pipeline, wn-prototype-card
│   │   └── icons.js                  Lucide-derived icon paths.
│   └── index.js                      Side-effect import — registers all elements.
├── adapters/django/                  Layer 3 — optional Django partials.
├── examples/
│   ├── showcase/index.html           Every component on one page. Playwright target.
│   └── whynot-control/index.html     Full app composition (React + custom elements).
└── assets/                           Logo, mark.

Open this locally

pnpm install
pnpm showcase        # then visit http://localhost:4321/examples/showcase/
pnpm example         # then visit http://localhost:4322  (whynot-control kit)

The remainder of this README is the full design language — colour reasoning, type stack, content rules, iconography. Treat it as authoritative.

CONTENT FUNDAMENTALS

Voice

The voice is quiet, structured, evidence-oriented, and careful about overclaiming. It comes directly out of the AGENT_RULES.md and OPERATING_MODEL.md:

Agent outputs should be concise, evidence-oriented, explicit about uncertainty, and careful to separate idea, hypothesis, signal, and decision.

whynot-control/AGENT_RULES.md

The voice should sound like a careful field-notebook — not a startup landing page, not a product manifesto, not marketing copy.

Casing

  • Sentence case for headings, buttons, labels, links — not Title Case. ("Smallest useful test", not "Smallest Useful Test".)
  • lowercase for the organization name in body: whynot, never WhyNot or WHYNOT. The logo wordmark may use WhyWhyNot for legacy reasons.
  • UPPERCASE is reserved for short eyebrow labels (PROTOTYPE, SIGNAL S2, STAGE, IN BETA) — set in mono, with letterspacing.
  • code-case for repo names, doc names, folder names: whynot-control, INTENT.md, inbox/. Always in monospace.

Person

Third person, with the project as subject. Avoid "we", avoid "you", avoid "I".

  • "A prototype is a question made tangible."
  • "The repository helps the user capture unusual but potentially useful ideas."
  • "We help you discover weird ideas."
  • "You'll love how easy this is."

Direct second-person is reserved for imperatives in a checklist (e.g. "Read INTENT.md.").

Tone

  • Curious, not enthusiastic. "This may be worth a closer look" beats "🚀 huge if true!".
  • Hedged, not promotional. Use may, could, seems, appears to. Avoid will, guaranteed, the best.
  • Distinguish idea / hypothesis / signal / decision. Never collapse them.
  • Lack of signal is also information. Silence is a finding, not a failure.

Phrasing patterns to imitate

Lifted from the existing control documents:

  • "A prototype is a question made tangible."
  • "Signal beats enthusiasm."
  • "Signals are evidence, not vibes."
  • "Capture is not commitment."
  • "Low-cost learning first."
  • "A prototype can be interesting and still be parked."

The pattern is: short declarative claim → small qualifier or counter-claim. Two beats, no exclamation.

Phrasing to avoid

  • "Revolutionize…", "Reimagine…", "Unlock…", "Empower…"
  • "🚀", "", "🔥", any other hype emoji.
  • "We believe…", "We're on a mission to…"
  • "Beta — sign up now!" (use "Closed beta. Invitation only.")
  • Numbers without context ("10x faster"). Use signal-record format instead.

Examples

Avoid Prefer
"Get started — it's free!" "Inbox is open. Capture is not commitment."
"Our amazing new prototype" "Prototype WNO-014. Stage: experiment."
"Users love it!" "S2 — repeated interest, three concrete use-cases."
"Coming soon — sign up!" "Closed beta. Five seats. Ends 2026-04-01."
"🎉 Launched!" "Promoted to Helix on 2026-03-12. See DECISIONS.md."

Emoji & punctuation

  • No emoji in body copy, headings, or UI.
  • ? and ! are the brand's punctuation — they appear in the logo and may appear, sparingly, in display headlines (try($idea) until success;, why? why not!).
  • (U+2192) for "promotes to / goes to" links between stages. Not ->.
  • (em dash) for parenthetical, not --.

VISUAL FOUNDATIONS

Overall posture

The system reads like engineering graph paper — precise hairlines, lots of whitespace, monospace labels in margins, content blocks that look like fields in a form rather than cards in a feed. The aesthetic is closer to a Bauhaus wall-chart or a man page than to a SaaS dashboard.

Color

  • Mostly black on white, with a few flat greys.
  • One accent only: a warm yellow (--hi: #FFE14A) lifted from the LEGO brick in the logo. It appears as highlighter / annotation / signal-marker — never as a button fill, never as a hero background.
  • No gradients. Anywhere. Including subtle ones.
  • No tinted whites--paper is a true #FFFFFF; --paper-2 and --paper-3 are barely-warm off-whites (#FAFAF7, #F4F4EF) reserved for sheets and recessed code blocks.
  • Status colors (S0S4 signal strength) are rendered as desaturated grey ramps, not red/yellow/green. S4 ("commercial signal") is the only one that uses the yellow accent, because that's the threshold where a prototype actually matters commercially.

Type

  • Family: IBM Plex Sans for everything UI/body. IBM Plex Mono for labels, code, and stage markers. IBM Plex Serif for the occasional editorial pull-quote. (See font substitution note in Fonts below.)
  • Weights: 300 (display only), 400 (body), 500 (UI / headings), 600 (occasional emphasis). Never 700+ — too marketing.
  • Tracking: tight on display (-0.035em), neutral on body, wide on uppercase labels (0.08em — this is the one signature move).
  • Eyebrows everywhere: short uppercase mono labels above titles (STAGE, SIGNAL, PROTOTYPE). They are the system's main rhythmic element.

Spacing

  • 4px base unit, exposed as --sp-1 (4) through --sp-10 (128).
  • Generous: a content block typically has --sp-7 (48px) of internal padding. Lists separated by --sp-5 (24px) minimum.
  • Section breaks are big (--sp-9, 96px). Reads like a printed report.

Backgrounds

  • --paper (#FFFFFF) by default. Period.
  • --paper-2 for full-width "sheet" sections (e.g. between hero and content).
  • --paper-3 for recessed surfaces only — code blocks, inline pre, inset cards.
  • No images as backgrounds. No hand-drawn illustrations. No repeating patterns. No textures. No noise. No grain. (Exception: a 1px hairline grid may be used on a literal "wireframe" mock; see preview/grid-paper.html.)

Animation

  • Minimal. This is a document system, not a product UI.
  • Transitions on hover only: 120ms ease on text-decoration-color for links, border-color for inputs.
  • No spring physics, no bounce, no fade-in-on-scroll. Anything that draws attention is not in the spirit of "low-cost learning first".
  • Exception: the cursor-blink animation on a <TerminalLine> is permitted because it's a literal terminal motif.

Hover & press states

  • Links — underline color goes from --border-strong to --fg-1 on hover.
  • Buttons (primary, dark)--ink--ink-2 on hover. On press, no transform, just --ink (back to baseline).
  • Buttons (secondary, outline) — border --border--ink. On press, background flashes --bg-3.
  • Cardsdo not have hover states. They are documents, not interactive surfaces. Exception: prototype cards in an index list get a 1px black left border on hover.
  • No scale transforms, no shadow lifts, no glow effects.

Borders

  • 1px solid --border (#E5E5E2) is the default. Used everywhere.
  • --border-strong (#C9C9C5) for section dividers and the outline of a primary block.
  • Hairline --border-soft (#F0F0EC) for internal rules within a card.
  • No double borders, no inset borders, no dashed borders except in one specific case: dashed --border-strong is used to indicate "placeholder / not yet defined" content (see Components / Empty State).

Shadows

  • --shadow-0: none. This is the default. Most cards and panels have no shadow.
  • --shadow-1: 0 1px 0 var(--line) — a 1px bottom-line, used in place of bottom-border on sticky headers.
  • --shadow-2: 0 1px 0 var(--line-strong) — slightly stronger version.
  • --shadow-3 is reserved for floating elements only (a popover, a focus-trapped modal). Even then it's a soft 4-12px diffuse shadow at 10% opacity — never a "card lift" shadow.

Protection gradients vs capsules

  • No protection gradients. Backgrounds are solid; never overlay a gradient to "rescue" text from a busy background, because backgrounds are never busy.
  • Capsules (pills with rounded ends) are used only for --label and tag elements — never for buttons. Buttons are slightly rounded rectangles (--r-2, 4px).

Layout rules

  • Single-column reading width of ~640px for body, ~880px for documents with sidebars.
  • Constant page padding of --sp-7 (48px) on desktop, --sp-4 (16px) on mobile.
  • Sticky element: top navigation bar, height 56px, bottom hairline.
  • Sidebar (where used): 256px fixed width, --paper-2 background, no border on right (uses whitespace to separate from main).
  • Grids for structured data only — never as a "card wall". 12-column with 24px gutters.

Transparency & blur

  • Almost never used. No frosted glass. No backdrop-filter.
  • One permitted use: a rgba(255,255,255,0.92) on the sticky top nav so that scrolled content is faintly visible behind it. No blur.

Imagery

  • Black & white only. All photography (when used) is rendered with filter: grayscale(1) contrast(0.95) — slightly low-contrast, like a Risograph print.
  • Aspect ratios: 4:3 (preferred — feels like a document figure), 1:1 (for portraits / icons). Never 16:9 in body.
  • No people-stock-photography. Prefer objects, diagrams, or screenshots.
  • No AI-generated imagery unless explicitly labelled as such with a [generated] caption.

Corner radii

  • --r-1 (2px) for inputs and tags.
  • --r-2 (4px) for buttons and small cards.
  • --r-3 (8px) for large cards and modals.
  • --r-pill (999px) for label capsules only.
  • --r-0 (0px / square) is the default for documents, sheets, and any element wider than ~600px. Big things are square.

Cards

A "card" in this system is a bordered rectangle, not a shadowed object floating off the page.

  • 1px --border outline.
  • --paper background.
  • 4px or 8px radius depending on size.
  • No shadow.
  • Internal padding --sp-5 (24px) minimum.
  • A monospace eyebrow at top-left + a stage label at top-right is the canonical card header.
  • On hover (when interactive): the top-left eyebrow tints to --fg-1, and a 2px black bar appears flush against the left edge. Nothing else moves.

ICONOGRAPHY

The codebase did not ship an icon font, an icon sprite, or any SVG icons — whynot-control is a documents-only repo. The only visual asset is the LEGO-brick logo.

Approach

  • Lucide icons via CDN is the chosen icon set. It matches the system's stroke weight (1.5px), neutral geometry, and "wireframe artefact" feel better than Material, Heroicons, or Phosphor. Load with <script src="https://unpkg.com/lucide@latest"></script> and call lucide.createIcons().
  • Stroke weight is always 1.5px. Override Lucide's default 2px via stroke-width="1.5" on each <svg> or via CSS.
  • Color: currentColor. Icons inherit from text. No two-tone, no fills.
  • Size: 16px (inline), 20px (button), 24px (heading-adjacent), 32px (feature). Never larger — large icons read as decoration, and decoration is not in the spirit.

When to use icons

  • In navigation labels, button labels, and inline status — only when the icon adds parsing speed. If the word reads faster than the icon, skip the icon.
  • In document margins to mark stage transitions (e.g. → Helix).
  • Not as decorative chrome on cards.
  • Not as "feature icons" in a 3-up grid on a marketing page (this system has no marketing pages).

Emoji

  • Never. Emoji are not used anywhere — in copy, labels, alt text, or as fallback for missing icons.

Unicode characters used as icons

These are allowed and preferred over raster icons in some contexts:

Char Used for
"promotes to" / pipeline arrow
back / previous
· inline bullet separator (in nav, in meta lines)
em dash, in metadata
? ! the brand's signature punctuation, in display headlines only
§ section marker, in long documents
  • The primary mark is the LEGO brick with ? and ! underneath, in pure black & white. assets/whynot-logo.png (300×300 raster, transparent background).
  • For very small uses (favicon, footer mark), a simplified mark is recommended: just the ?! pair in IBM Plex Sans 600, with the brick implied by a 2×4 dot grid above. See preview/logo.html.
  • The brick should never be coloured (no LEGO-red, LEGO-blue, etc). It is always black-outlined on white, or white-outlined on black.
  • Minimum size: 32px square. Below that, fall back to the ?! wordmark.

A note on font substitution

The control repo did not ship font files. IBM Plex Sans / Mono / Serif were chosen as a fresh pairing because:

  • The "Plex" family was designed by IBM as an explicitly neutral, technical-document family — the same use-case as this system.
  • All three (sans, mono, serif) share metrics, so they mix cleanly in templates and tables.
  • They are openly licensed (SIL OFL) and available on Google Fonts.

Plex is currently loaded from Google Fonts (see top of colors_and_type.css). For offline use, drop the .woff2 files into fonts/ and swap the @import for a local @font-face block.

🟨 Substitution flagged: there was no specified brand font; IBM Plex is a choice made here. If whynot later adopts a different brand font, replace --ff-sans / --ff-mono / --ff-serif in colors_and_type.css and everything downstream will follow.

Description
A design system for clean neutral prototyping and early production.
Readme 207 KiB
Languages
JavaScript 66.4%
CSS 29.5%
HTML 4.1%