feat(design): batch A recorded, batch B in the design lab (E28 S2b)

DESIGN.md: the Controls chapter decided (A1-A6) with the decision-log rows;
density stays open as B1. The lab keeps only the chosen A renderings as
reference and adds batch B: density, the scheme set (measured accent hue
gaps and light contrast), state layers, the page-chrome model, surface per
role, the public watch page, the nav rail, the loading form and where the
header + fade edge goes.
This commit is contained in:
2026-09-24 02:21:20 +02:00
parent 50923bb684
commit 477271907e
7 changed files with 1481 additions and 274 deletions
+27 -11
View File
@@ -1,15 +1,21 @@
import { useLayoutEffect, useState } from "react";
import clsx from "clsx";
import { SCHEMES, type Scheme } from "../lib/theme";
import { A1, A2, A3, A4, A5, A6 } from "./batchA";
import { BatchA } from "./batchA";
import { B1, B2, B3 } from "./batchB";
import { B4, B5, B6, B7, B8, B9 } from "./batchBChrome";
const BATCH_A = [
{ id: "a1", code: "A1", title: "Button shape", render: A1 },
{ id: "a2", code: "A2", title: "Icon buttons", render: A2 },
{ id: "a3", code: "A3", title: "Touch size", render: A3 },
{ id: "a4", code: "A4", title: "Input", render: A4 },
{ id: "a5", code: "A5", title: "Dropdown trigger", render: A5 },
{ id: "a6", code: "A6", title: "Checkbox / switch / slider", render: A6 },
// The open decisions, in the order they are asked. The decided ones render below them as reference.
const OPEN = [
{ id: "b1", code: "B1", title: "Density", render: B1 },
{ id: "b2", code: "B2", title: "Schemes", render: B2 },
{ id: "b3", code: "B3", title: "Hover", render: B3 },
{ id: "b4", code: "B4", title: "Page chrome", render: B4 },
{ id: "b5", code: "B5", title: "Surfaces", render: B5 },
{ id: "b6", code: "B6", title: "/watch", render: B6 },
{ id: "b7", code: "B7", title: "Nav rail", render: B7 },
{ id: "b8", code: "B8", title: "Loading", render: B8 },
{ id: "b9", code: "B9", title: "Fade", render: B9 },
] as const;
// The design lab (E28): the living style guide. Dev-only — see main.tsx and devOnlyGuard.ts. It sits
@@ -97,7 +103,7 @@ export default function DesignLab() {
/>
</label>
<nav className="flex flex-wrap gap-1 text-xs">
{BATCH_A.map((d) => (
{OPEN.map((d) => (
<a
key={d.id}
href={`#${d.id}`}
@@ -106,20 +112,30 @@ export default function DesignLab() {
{d.code} {d.title}
</a>
))}
<a href="#decided" className="rounded-md px-1.5 py-0.5 text-success hover:bg-card">
A ✓
</a>
</nav>
</div>
<main className="mx-auto flex max-w-screen-2xl flex-col gap-6 p-4">
<header>
<h1 className="text-2xl font-bold">Batch A · controls</h1>
<h1 className="text-2xl font-bold">Batch B · surfaces, chrome and colour</h1>
<p className="text-sm text-muted">
Each decision shows its variants with the app&apos;s real CSS. The framed one is the
recommendation. Resize the window to tablet size to see them there too.
</p>
</header>
{BATCH_A.map((d) => (
{OPEN.map((d) => (
<d.render key={d.id} />
))}
<header id="decided" className="mt-6 scroll-mt-20">
<h2 className="text-xl font-bold">Decided · batch A, controls</h2>
<p className="text-sm text-muted">
The chosen rendering of each decision, the reference the primitives are built to.
</p>
</header>
<BatchA />
</main>
</div>
);