348 lines
16 KiB
HTML
348 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>@whynot/design — Showcase</title>
|
|
<link rel="icon" href="../../assets/whynot-logo.png">
|
|
<link rel="stylesheet" href="../../src/styles/colors_and_type.css">
|
|
<link rel="stylesheet" href="../../src/styles/components.css">
|
|
|
|
<!-- Lit comes via importmap. In a real consumer this would be bundled. -->
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"lit": "https://esm.sh/lit@3.2.1",
|
|
"lit/": "https://esm.sh/lit@3.2.1/"
|
|
}
|
|
}
|
|
</script>
|
|
<script type="module" src="../../src/index.js"></script>
|
|
|
|
<style>
|
|
body { background: var(--paper); }
|
|
.page { max-width: 1080px; margin: 0 auto; padding: 48px 32px 96px; display: flex; flex-direction: column; gap: 64px; }
|
|
section { display: flex; flex-direction: column; gap: 16px; }
|
|
section > h2 { font: 500 22px/1.25 var(--ff-sans); margin: 0; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
|
|
section > p.lede { margin: 0; max-width: 60ch; color: var(--fg-2); }
|
|
.demo { display: flex; gap: 16px; flex-wrap: wrap; padding: 24px; border: 1px solid var(--border); border-radius: 4px; background: var(--paper-2); align-items: flex-start; }
|
|
.demo--grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
|
.demo--stack { flex-direction: column; align-items: stretch; }
|
|
.label { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); margin-bottom: 6px; display: block; }
|
|
[data-meta] { font: 400 11px var(--ff-mono); color: var(--fg-3); margin-top: 8px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<wn-top-nav logo-src="../../assets/whynot-logo.png" brand="whynot" slug="design / showcase">
|
|
<a slot="links" class="wn-topnav__link wn-topnav__link--active" href="#">Showcase</a>
|
|
<a slot="links" class="wn-topnav__link" href="../whynot-control/index.html">whynot-control kit</a>
|
|
<wn-search-input slot="right" placeholder="Find a component…"></wn-search-input>
|
|
<wn-button slot="right" variant="primary" icon="plus">New idea</wn-button>
|
|
</wn-top-nav>
|
|
|
|
<main class="page">
|
|
<wn-page-header
|
|
eyebrow="@whynot/design · v0.2.0"
|
|
title="Component showcase"
|
|
lede="Every web component the design system currently ships, in one scrollable page. Use this as the canonical visual reference and as the Playwright regression target. Every section also doubles as copy-pasteable HTML for a Django template.">
|
|
<wn-button slot="actions" variant="secondary" icon="arrow-left" href="../whynot-control/index.html">Back to UI kit</wn-button>
|
|
</wn-page-header>
|
|
|
|
<!-- ATOMS -->
|
|
<section>
|
|
<h2>Atoms — buttons</h2>
|
|
<p class="lede">Three variants, three sizes. Primary is ink-on-paper; secondary is the outline default; ghost has no chrome until hover.</p>
|
|
<div class="demo">
|
|
<wn-button variant="primary">Promote prototype</wn-button>
|
|
<wn-button>Park</wn-button>
|
|
<wn-button variant="ghost">View signal</wn-button>
|
|
<wn-button variant="primary" disabled>Disabled</wn-button>
|
|
</div>
|
|
<div class="demo">
|
|
<wn-button variant="primary" icon="arrow-right">Promote</wn-button>
|
|
<wn-button icon="archive">Park</wn-button>
|
|
<wn-button variant="ghost" icon="x">Dismiss</wn-button>
|
|
<wn-button size="sm" variant="primary">Small</wn-button>
|
|
<wn-button size="lg" variant="primary">Large</wn-button>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Atoms — tags, eyebrows, stamps</h2>
|
|
<div class="demo">
|
|
<wn-eyebrow>Stage 3 · Experiment</wn-eyebrow>
|
|
<wn-eyebrow strong>Signal · S2</wn-eyebrow>
|
|
<wn-stamp>Draft · WNO-014</wn-stamp>
|
|
</div>
|
|
<div class="demo">
|
|
<wn-tag>Raw Idea</wn-tag>
|
|
<wn-tag>Prototype Candidate</wn-tag>
|
|
<wn-tag active>Experiment</wn-tag>
|
|
<wn-tag>Promotion Candidate</wn-tag>
|
|
<wn-tag>Parked</wn-tag>
|
|
<wn-tag draft>Draft</wn-tag>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Atoms — stage dots (signal strength)</h2>
|
|
<p class="lede">Desaturated by design. S4 is the only level that uses the yellow accent — the threshold where a prototype actually matters commercially.</p>
|
|
<div class="demo">
|
|
<wn-stage-dot level="S0">S0 · No signal</wn-stage-dot>
|
|
<wn-stage-dot level="S1">S1 · Weak</wn-stage-dot>
|
|
<wn-stage-dot level="S2">S2 · Medium</wn-stage-dot>
|
|
<wn-stage-dot level="S3">S3 · Strong</wn-stage-dot>
|
|
<wn-stage-dot level="S4">S4 · Commercial</wn-stage-dot>
|
|
</div>
|
|
<h2 style="margin-top: 32px;">Atoms — phase dots</h2>
|
|
<p class="lede">Numbered phases, distinct from signal strength. Used in pipelines, checklists, onboarding flows.</p>
|
|
<div class="demo">
|
|
<wn-phase-dot state="done" num="1">Triage</wn-phase-dot>
|
|
<wn-phase-dot state="done" num="2">Card</wn-phase-dot>
|
|
<wn-phase-dot state="active" num="3">Experiment</wn-phase-dot>
|
|
<wn-phase-dot state="todo" num="4">Signal review</wn-phase-dot>
|
|
<wn-phase-dot state="warn" num="!">Blocked</wn-phase-dot>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Atoms — icons</h2>
|
|
<p class="lede">Lucide subset, inlined as paths. Stroke 1.5px, inherits <code>currentColor</code>. Add new icons in <code>src/elements/icons.js</code> — not in consuming repos.</p>
|
|
<div class="demo" style="gap: 24px;">
|
|
<wn-icon name="inbox" size="lg"></wn-icon>
|
|
<wn-icon name="lightbulb" size="lg"></wn-icon>
|
|
<wn-icon name="flask-conical" size="lg"></wn-icon>
|
|
<wn-icon name="activity" size="lg"></wn-icon>
|
|
<wn-icon name="users" size="lg"></wn-icon>
|
|
<wn-icon name="git-branch" size="lg"></wn-icon>
|
|
<wn-icon name="check-square" size="lg"></wn-icon>
|
|
<wn-icon name="archive" size="lg"></wn-icon>
|
|
<wn-icon name="arrow-right" size="lg"></wn-icon>
|
|
<wn-icon name="search" size="lg"></wn-icon>
|
|
<wn-icon name="filter" size="lg"></wn-icon>
|
|
<wn-icon name="settings" size="lg"></wn-icon>
|
|
<wn-icon name="circle-help" size="lg"></wn-icon>
|
|
<wn-icon name="circle-alert" size="lg"></wn-icon>
|
|
<wn-icon name="circle-check" size="lg"></wn-icon>
|
|
<wn-icon name="more-horizontal" size="lg"></wn-icon>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- FORMS -->
|
|
<section>
|
|
<h2>Form — inputs</h2>
|
|
<div class="demo demo--grid">
|
|
<wn-field-row label="Prototype name">
|
|
<wn-input name="name" placeholder="e.g. relevant-coronapolitics-timeline" required></wn-input>
|
|
</wn-field-row>
|
|
<wn-field-row label="One-line pitch">
|
|
<wn-input name="pitch" value="Discover the weird and the useful." help="120 char limit · plain sentence"></wn-input>
|
|
</wn-field-row>
|
|
<wn-field-row label="Learning question">
|
|
<wn-textarea name="learning" rows="3">What would we need to learn to know whether this idea deserves another step?</wn-textarea>
|
|
</wn-field-row>
|
|
<wn-field-row label="Smallest useful test">
|
|
<wn-input error error-text="Required — describe in one sentence."></wn-input>
|
|
</wn-field-row>
|
|
<wn-field-row label="Promotion target">
|
|
<wn-select name="target">
|
|
<option value="">— Select a target —</option>
|
|
<option value="helix">Helix</option>
|
|
<option value="coulomb" selected>Coulomb</option>
|
|
<option value="plenitude">Plenitude</option>
|
|
<option value="binky">Binky</option>
|
|
<option value="tegwick">Tegwick</option>
|
|
</wn-select>
|
|
</wn-field-row>
|
|
<wn-field-row label="Search">
|
|
<wn-search-input placeholder="Search ideas, prototypes, signals…"></wn-search-input>
|
|
</wn-field-row>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- LAYOUT -->
|
|
<section>
|
|
<h2>Layout — cards</h2>
|
|
<div class="demo demo--grid">
|
|
<wn-card>
|
|
<wn-eyebrow slot="header">Signal · S2</wn-eyebrow>
|
|
<wn-stage-dot slot="header" level="S2">Medium</wn-stage-dot>
|
|
<p>Three founders independently asked for "something to capture the half-formed stuff" in yesterday's calls.</p>
|
|
<span slot="footer">2026-03-02</span>
|
|
<span slot="footer">interview</span>
|
|
</wn-card>
|
|
<wn-card variant="inset" clickable>
|
|
<wn-eyebrow slot="header">Decision · DEC-002</wn-eyebrow>
|
|
<wn-tag slot="header" draft>Open</wn-tag>
|
|
<p>Maintain A1 Incubating until first prototype candidates review.</p>
|
|
<span slot="footer">→ pending</span>
|
|
<span slot="footer">whynot-control</span>
|
|
</wn-card>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — prototype card (composite)</h2>
|
|
<div class="demo demo--grid">
|
|
<wn-prototype-card card-id="WNO-014" signal="S1" stage-label="Experiment">
|
|
<span slot="pitch">A field-notebook for catching weird ideas before they evaporate.</span>
|
|
<span slot="learning">Do people return to capture more than once?</span>
|
|
<span slot="test">One-page landing + email capture, 14 days.</span>
|
|
<span slot="target">→ Coulomb</span>
|
|
</wn-prototype-card>
|
|
<wn-prototype-card card-id="WNO-017" signal="S3" stage-label="Signal review" href="#">
|
|
<span slot="pitch">A LEGO-brick mood board for engineers who don't think in mood boards.</span>
|
|
<span slot="learning">Will engineers attach metaphors to their tickets?</span>
|
|
<span slot="test">Slack bot, three teams, two weeks.</span>
|
|
<span slot="target">→ Helix</span>
|
|
</wn-prototype-card>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — pipeline</h2>
|
|
<div class="demo demo--stack">
|
|
<wn-pipeline active-idx="3"></wn-pipeline>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — table</h2>
|
|
<div class="demo demo--stack" style="padding: 0; background: var(--paper); display: block;">
|
|
<wn-table id="signals-table">
|
|
<wn-table-row>
|
|
<wn-table-cell variant="mono">SIG-031</wn-table-cell>
|
|
<wn-table-cell variant="mono">WNO-017</wn-table-cell>
|
|
<wn-table-cell><wn-stage-dot level="S3"></wn-stage-dot></wn-table-cell>
|
|
<wn-table-cell>Two teams shipped public README sections labelled "brick: scope" after using the bot for a week.</wn-table-cell>
|
|
<wn-table-cell variant="meta">usage log</wn-table-cell>
|
|
<wn-table-cell variant="meta">2026-03-04</wn-table-cell>
|
|
</wn-table-row>
|
|
<wn-table-row>
|
|
<wn-table-cell variant="mono">SIG-030</wn-table-cell>
|
|
<wn-table-cell variant="mono">WNO-017</wn-table-cell>
|
|
<wn-table-cell><wn-stage-dot level="S2"></wn-stage-dot></wn-table-cell>
|
|
<wn-table-cell>Three engineers DM'd asking for an export-to-Notion option.</wn-table-cell>
|
|
<wn-table-cell variant="meta">Slack</wn-table-cell>
|
|
<wn-table-cell variant="meta">2026-03-03</wn-table-cell>
|
|
</wn-table-row>
|
|
<wn-table-row>
|
|
<wn-table-cell variant="mono">SIG-029</wn-table-cell>
|
|
<wn-table-cell variant="mono">WNO-014</wn-table-cell>
|
|
<wn-table-cell><wn-stage-dot level="S1"></wn-stage-dot></wn-table-cell>
|
|
<wn-table-cell>Landing page: 34 visits, 7 emails, 0 returns in week 1.</wn-table-cell>
|
|
<wn-table-cell variant="meta">Plausible</wn-table-cell>
|
|
<wn-table-cell variant="meta">2026-03-01</wn-table-cell>
|
|
</wn-table-row>
|
|
</wn-table>
|
|
<script>
|
|
// Columns are set via the property (so we can pass an array).
|
|
document.getElementById("signals-table").columns = [
|
|
{ label: "ID", width: 90 },
|
|
{ label: "Prototype", width: 100 },
|
|
{ label: "Level", width: 80 },
|
|
{ label: "What happened" },
|
|
{ label: "Source", width: 110 },
|
|
{ label: "Date", width: 100 },
|
|
];
|
|
</script>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — banners</h2>
|
|
<div class="demo demo--stack">
|
|
<wn-banner variant="info" title="Heads up">A prototype can be interesting and still be parked. Capture is not commitment.</wn-banner>
|
|
<wn-banner variant="success" title="Promoted" dismissible>WNO-017 was promoted to Helix on 2026-03-12. See DECISIONS.md.</wn-banner>
|
|
<wn-banner variant="warn" title="Weak signal" dismissible>Landing page: 12 visits in 30 days, 0 returns. Consider parking this prototype.</wn-banner>
|
|
<wn-banner variant="error" title="CI failed">Visual regression failed on examples/showcase. Two snapshots changed.</wn-banner>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — empty state</h2>
|
|
<div class="demo demo--stack">
|
|
<wn-empty-state icon="activity" title="No signals yet.">
|
|
Lack of signal is also information.
|
|
<wn-button slot="cta" variant="primary" icon="plus">Record a signal</wn-button>
|
|
</wn-empty-state>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — breadcrumb</h2>
|
|
<div class="demo demo--stack">
|
|
<wn-breadcrumb>
|
|
<a href="#">whynot</a>
|
|
<a href="#">Prototypes</a>
|
|
<span>WNO-014</span>
|
|
</wn-breadcrumb>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — modal (click to open)</h2>
|
|
<div class="demo">
|
|
<wn-button variant="primary" id="open-modal">Open dialog</wn-button>
|
|
<wn-modal id="my-modal" title="Park this prototype?">
|
|
<p>Parking is not rejection. The card stays in <code>prototypes/</code> with stage <code>parked</code>; it can be resumed at any time.</p>
|
|
<p>If a new signal arrives, you'll see it in this prototype's signal log even while parked.</p>
|
|
<wn-button slot="footer" id="cancel-modal">Cancel</wn-button>
|
|
<wn-button slot="footer" variant="primary" icon="archive" id="confirm-park">Park prototype</wn-button>
|
|
</wn-modal>
|
|
</div>
|
|
<script>
|
|
const modal = document.getElementById("my-modal");
|
|
document.getElementById("open-modal").addEventListener("click", () => modal.open = true);
|
|
document.getElementById("cancel-modal").addEventListener("click", () => modal.open = false);
|
|
document.getElementById("confirm-park").addEventListener("click", () => modal.open = false);
|
|
</script>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Layout — toast region (click to show)</h2>
|
|
<div class="demo">
|
|
<wn-button id="show-toast" variant="primary">Show toast</wn-button>
|
|
</div>
|
|
<wn-toast-region id="toasts"></wn-toast-region>
|
|
<script>
|
|
document.getElementById("show-toast").addEventListener("click", () => {
|
|
const region = document.getElementById("toasts");
|
|
const t = document.createElement("wn-toast");
|
|
t.setAttribute("variant", "success");
|
|
t.setAttribute("title", "Captured");
|
|
t.setAttribute("dismissible", "");
|
|
t.textContent = "Idea saved to inbox/. Capture is not commitment.";
|
|
region.appendChild(t);
|
|
setTimeout(() => t.remove(), 4000);
|
|
});
|
|
</script>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Chrome — sidebar (illustrative slice)</h2>
|
|
<p class="lede">Normally lives inside <code>.wn-app</code> alongside the main column. Shown here as a standalone fragment.</p>
|
|
<div class="demo" style="padding: 0;">
|
|
<wn-sidebar activation="A1 · Incubating" style="height: auto; position: static; width: 240px;">
|
|
<wn-sidebar-group label="Work">
|
|
<wn-sidebar-item icon="inbox" href="#" count="7">Inbox</wn-sidebar-item>
|
|
<wn-sidebar-item icon="flask-conical" href="#" active count="4">Prototypes</wn-sidebar-item>
|
|
<wn-sidebar-item icon="activity" href="#" count="12">Signals</wn-sidebar-item>
|
|
<wn-sidebar-item icon="users" href="#" count="1">Betas</wn-sidebar-item>
|
|
<wn-sidebar-item icon="check-square" href="#" count="3">Decisions</wn-sidebar-item>
|
|
</wn-sidebar-group>
|
|
<wn-sidebar-group label="Control docs">
|
|
<wn-sidebar-item variant="doc" icon="file-text" href="#">INTENT.md</wn-sidebar-item>
|
|
<wn-sidebar-item variant="doc" icon="file-text" href="#">SCOPE.md</wn-sidebar-item>
|
|
<wn-sidebar-item variant="doc" icon="file-text" href="#">OPERATING_MODEL.md</wn-sidebar-item>
|
|
</wn-sidebar-group>
|
|
</wn-sidebar>
|
|
</div>
|
|
</section>
|
|
|
|
<section style="opacity: 0.7;">
|
|
<p data-meta>End of showcase. See <code>examples/whynot-control/index.html</code> for a full app composition.</p>
|
|
</section>
|
|
</main>
|
|
</body>
|
|
</html>
|