Files
whynot-design/designbook/preview/page-landing-auth.html
tegwick 0d688ca94a feat(designbook): technology-neutral IR + stack-adapter pipeline (WHYNOT-WP-0002 T01-T06)
Author the design language once in the canonical React designbook and project it
one-way onto each stack: React -> designbook/ -> ir/ -> adapters/<stack>/.

Phase 0 — contracts & governance (T01-T03):
- ir/SCHEMA.md + ir/schema/{component,tokens}.schema.json — neutral IR contract
  (W3C DTCG tokens; React prop -> HTML attribute mapping; non-portable props flagged).
- adapters/ADAPTER_CONTRACT.md — inputs, drift-report + parity-result shapes,
  idempotency rules, CI exit codes (0 ok / 2 usage / 3 drift / 4 parity / 5 internal).
- .claude/rules/designbook-propagation.md + DesignSystemIntroduction.md §5.1 —
  one-way directionality + drift-resolution workflow.

T04 — canonical React designbook + the missing pull tool:
- The bundled /design-sync skill only PUSHES repo->cloud; it cannot populate
  designbook/. Added scripts/designbook_pull.py + `make designbook-pull`, which drives
  the local claude binary headless (acceptEdits) so DesignSync fetch+write runs in a
  subprocess (contents never hit the orchestrator's context). Pulled 44 files;
  excludes the _whynot-design-seed/ self-copy. Corrected the docs that wrongly called
  /design-sync the pull.

T05 — IR extractor (scripts/ir-extract.mjs + `make ir`):
- ir/tokens.json (80 tokens, DTCG, var() -> {ref} alias resolution); ir/components/*.json
  (10 contracts parsed from .jsx signatures: enum/boolean/number inference, prop->attr
  map, style/callback marked non-portable); ir/exemplars/*.

T06 — Lit token adapter (adapters/lit/ + `make adapt-lit`):
- Full-gen tokens into src/styles/colors_and_type.css :root (marker-bounded, idempotent
  no-op on re-run; hand-authored type CSS preserved).

NOTE: token regen synced Lit to canonical React — fonts IBM Plex -> system stacks and 8
status tokens added. This is a VISUAL change: review and run `pnpm test:visual:update`
before merge. Remaining: T07 scaffold+drift, T08 parity, T09 runbook, T10 2nd-adapter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 12:36:24 +02:00

225 lines
9.4 KiB
HTML

<!doctype html>
<!-- @dsCard group="Pages" name="Pages · Landing — Login & Registration" subtitle="Public landing · log in / request access toggle" viewport="1280x820" -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>whynot — landing</title>
<link rel="icon" href="../assets/whynot-logo.png">
<link rel="stylesheet" href="../colors_and_type.css">
<style>
html, body { height: 100%; }
body { margin: 0; background: var(--paper); color: var(--fg-1); }
/* faint engineering-graph backdrop, very subtle */
.page {
min-height: 100vh;
display: flex; flex-direction: column;
}
/* ---- top nav ---- */
.nav {
height: 60px; flex: none;
display: flex; align-items: center; gap: 24px;
padding: 0 40px;
border-bottom: 1px solid var(--line);
}
.nav .brand { display: flex; align-items: center; gap: 10px; }
.nav .brand img { width: 24px; height: 24px; }
.nav .brand .nm { font: 500 15px var(--ff-sans); letter-spacing: -0.01em; }
.nav .brand .slug { font: 400 12px var(--ff-mono); color: var(--fg-3); }
.nav .links { margin-left: auto; display: flex; align-items: center; gap: 24px; }
.nav .links a { font: 500 13px var(--ff-sans); color: var(--fg-2); text-decoration: none; }
.nav .links a:hover { color: var(--fg-1); }
.nav .pill {
font: 500 11px/1 var(--ff-mono); letter-spacing: 0.1em; text-transform: uppercase;
color: var(--fg-3); border: 1px solid var(--line); border-radius: 999px; padding: 6px 11px;
}
/* ---- hero split ---- */
.hero {
flex: 1; display: grid; grid-template-columns: 1.15fr 0.85fr;
align-items: stretch;
}
.pitch {
padding: 72px 56px 56px 40px;
display: flex; flex-direction: column; gap: 24px;
border-right: 1px solid var(--line);
background:
linear-gradient(to right, var(--line-soft) 1px, transparent 1px) 0 0 / 28px 28px,
linear-gradient(to bottom, var(--line-soft) 1px, transparent 1px) 0 0 / 28px 28px,
var(--paper-2);
}
.eyebrow-lg { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg-3); }
.pitch h1 {
font: 300 64px/0.98 var(--ff-sans); letter-spacing: -0.035em;
margin: 4px 0 0; color: var(--ink);
max-width: 13ch;
}
.pitch h1 .q { font-weight: 500; }
.pitch .lede { font: 400 18px/1.55 var(--ff-sans); color: var(--fg-2); margin: 0; max-width: 42ch; }
.codeline {
font: 500 14px var(--ff-mono); color: var(--fg-1);
background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-2);
padding: 12px 16px; align-self: flex-start;
}
.codeline .c { color: var(--ink-4); }
.codeline mark { background: var(--hi); color: var(--hi-ink); padding: 0 3px; }
.principles { margin-top: auto; display: flex; flex-direction: column; gap: 0; }
.principles .p {
display: grid; grid-template-columns: 28px 1fr; gap: 14px;
padding: 16px 0; border-top: 1px solid var(--line);
align-items: baseline;
}
.principles .p .k { font: 500 12px var(--ff-mono); color: var(--fg-3); }
.principles .p .v { font: 400 14px/1.5 var(--ff-sans); color: var(--fg-2); }
.principles .p .v b { color: var(--fg-1); font-weight: 500; }
/* ---- auth panel ---- */
.auth {
padding: 72px 40px 56px 56px;
display: flex; flex-direction: column;
max-width: 480px;
}
.auth .tabs { display: flex; gap: 0; border-bottom: 1px solid var(--line); margin-bottom: 28px; }
.auth .tab {
font: 500 13px var(--ff-sans); color: var(--fg-3); background: none; border: 0;
padding: 0 0 12px; margin-right: 28px; cursor: pointer;
border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.auth .tab.active { color: var(--fg-1); border-bottom-color: var(--ink); }
.form { display: flex; flex-direction: column; gap: 18px; }
.form.hidden { display: none; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field label { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); }
.field input, .field textarea {
font: 400 14px var(--ff-sans); color: var(--fg-1);
padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--r-1);
background: var(--paper); outline: none; transition: border-color 120ms ease;
}
.field input:focus, .field textarea:focus { border-color: var(--ink); }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-5); }
.field textarea { resize: none; min-height: 76px; font-family: var(--ff-sans); }
.field .row { display: flex; justify-content: space-between; align-items: baseline; }
.field .row a { font: 400 11px var(--ff-mono); color: var(--fg-3); text-decoration: none; }
.field .row a:hover { color: var(--fg-1); text-decoration: underline; }
.btn {
font: 500 14px var(--ff-sans); padding: 12px 18px; border-radius: var(--r-2);
border: 1px solid var(--ink); background: var(--ink); color: var(--paper);
cursor: pointer; transition: background 120ms ease; margin-top: 4px;
}
.btn:hover { background: var(--ink-2); }
.note {
font: 400 12px/1.5 var(--ff-mono); color: var(--fg-3);
margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--line-soft);
}
.note b { color: var(--fg-2); font-weight: 500; }
.footer {
flex: none; padding: 18px 40px; border-top: 1px solid var(--line);
display: flex; align-items: center; gap: 16px;
font: 400 11px var(--ff-mono); color: var(--fg-3); letter-spacing: 0.04em;
}
.footer .dot { width: 5px; height: 5px; border-radius: 999px; background: var(--ink-4); }
.footer .sp { margin-left: auto; }
</style>
</head>
<body>
<div class="page">
<nav class="nav">
<div class="brand">
<img src="../assets/whynot-logo.png" alt="">
<span class="nm">whynot</span>
<span class="slug">/ prototypes</span>
</div>
<div class="links">
<span class="pill">A1 · Incubating</span>
<a href="#" onclick="show('login');return false;">Log in</a>
</div>
</nav>
<div class="hero">
<!-- left: pitch -->
<section class="pitch">
<span class="eyebrow-lg">Prototype &amp; market-signal space</span>
<h1>why<span class="q">?</span> why not<span class="q">!</span></h1>
<p class="lede">A quiet workshop for discovering the weird and the useful — building, testing, and reviewing prototypes before they ever pretend to be products.</p>
<div class="codeline"><span class="c">$</span> try(<mark>$idea</mark>) until success<span class="c">;</span></div>
<div class="principles">
<div class="p"><span class="k">01</span><span class="v"><b>A prototype is a question made tangible.</b> Not a promise.</span></div>
<div class="p"><span class="k">02</span><span class="v"><b>Signal beats enthusiasm.</b> Evidence, not vibes.</span></div>
<div class="p"><span class="k">03</span><span class="v"><b>Capture is not commitment.</b> A good idea can still be parked.</span></div>
</div>
</section>
<!-- right: auth -->
<section class="auth">
<div class="tabs">
<button class="tab active" id="tab-login" onclick="show('login')">Log in</button>
<button class="tab" id="tab-register" onclick="show('register')">Request access</button>
</div>
<form class="form" id="form-login" onsubmit="return false;">
<div class="field">
<label for="li-email">Email</label>
<input id="li-email" type="email" placeholder="you@example.com" autocomplete="username">
</div>
<div class="field">
<div class="row">
<label for="li-pw">Password</label>
<a href="#" onclick="return false;">Forgot?</a>
</div>
<input id="li-pw" type="password" placeholder="••••••••" autocomplete="current-password">
</div>
<button class="btn" type="submit">Log in</button>
<p class="note">Access is limited to current contributors and invited beta participants. <b>No public sign-ups.</b></p>
</form>
<form class="form hidden" id="form-register" onsubmit="return false;">
<div class="field">
<label for="rg-name">Name</label>
<input id="rg-name" type="text" placeholder="What should we call you?">
</div>
<div class="field">
<label for="rg-email">Email</label>
<input id="rg-email" type="email" placeholder="you@example.com">
</div>
<div class="field">
<label for="rg-build">What would you want to build or test?</label>
<textarea id="rg-build" placeholder="One sentence is plenty. The weirder the better."></textarea>
</div>
<button class="btn" type="submit">Request invite</button>
<p class="note"><b>Closed beta. Invitation only.</b> Requests are read, not auto-approved — silence is also an answer.</p>
</form>
</section>
</div>
<footer class="footer">
<span class="dot"></span>
<span>whynot · 2026</span>
<span>·</span>
<span>Prereleases &amp; prototypes only</span>
<span class="sp">try($idea) until success;</span>
</footer>
</div>
<script>
function show(which) {
var isLogin = which === 'login';
document.getElementById('form-login').classList.toggle('hidden', !isLogin);
document.getElementById('form-register').classList.toggle('hidden', isLogin);
document.getElementById('tab-login').classList.toggle('active', isLogin);
document.getElementById('tab-register').classList.toggle('active', !isLogin);
}
</script>
</body>
</html>