diff --git a/claude/.claude/skills/ui-craft/SKILL.md b/claude/.claude/skills/ui-craft/SKILL.md new file mode 100644 index 0000000..3f9439b --- /dev/null +++ b/claude/.claude/skills/ui-craft/SKILL.md @@ -0,0 +1,255 @@ +--- +name: ui-craft +description: Build high-craft, polished CC2 screens — the layout/typography/state/status/motion/a11y and real-browser-verification discipline distilled from the read-only Order Outs rebuild. Use when building or polishing a view/card/panel, when a screen "looks bland," when covering all of a screen's states, or before calling any UI change done. +--- + +# High-craft UI + +The bar is a screen that reads as **data, not a form** — figure-on-ground +regions, one hero fact per unit, honest states, and geometry verified in a real +browser. This skill is the distilled playbook from the Order Outs rebuild +(`src/views/products-pricing/order-outs/`). + +**North stars — study these first, don't re-derive them:** + +- **Amit's evidence viewer** — the "list → focused detail" viewer host: + `src/views/evidence/EvidenceView.tsx` (collapses nav to maximize reading + space; drives detail from a focused-record store), + `src/views/evidence/evidence-bridge.ts` (viewer↔right-sidebar wiring). +- **Amit's underwriting tab** — one unified table of a loan's tasks + + requirements, row → modal/viewer, quiet accessible table chrome: + `src/views/readiness/ReadinessView.tsx`, + `src/views/readiness/ReadinessDataTable.tsx` (the shadcn data-table layout as + lightweight plain React; status-tone dots), + `src/views/readiness/FiledTasksPanel.tsx`. +- **shadcn v3** — the design language. The repo points to it by role, not by + name (`src/pages/docs-page.tsx` links the official docs): data-table = the + *tasks* example (`ReadinessDataTable`, `ApplicationDataTable`), shell = + the *dashboard* example (`docs/pylon-design-system.html`), and + `src/components/button-group-demo.tsx` shows the clone-into-owned-source + idiom. Compose these primitives; never re-skin `src/components/ui`. + +## When to use + +Building or polishing any view, card, panel, tray, or table under `src/`; the +human says a screen is "bland" or "so-so"; you need to cover every state of a +screen; or you are about to call a layout/spacing/wrapping change done. For the +correct *composition* of primitives (Card shapes, pinned Table, `as` rules) read +`src/components/AGENTS.md`; for the non-negotiable semantic-HTML/DOM rules read +`src/AGENTS.md`. This skill is the craft layer on top of those. + +## Before you build — ground the work, don't jump to code + +- **Study the best in-repo exemplar first, then write a short playbook.** The + strongest results came from reading Amit's evidence viewer + underwriting tab + and distilling their moves before touching the new screen — not from coding + straight away. Reuse `docs/screen-redesign-process.md` for the port workflow. +- **"It looks bland" is latitude to redesign, not a pixel tweak.** The Order + Outs "it looks so so bland… it doesn't need to be what I told you" triggered a + full rethink into rich dossier rows — treat that feedback as permission to + change structure. +- **Run a quick adversarial design panel before building.** Sketch 2–3 layouts + and judge them on craft / data-parity / a11y / buildability. Order Outs + rejected two-side-by-side-rails and a full-width single-column stack before + choosing list→right-tray; the ground-up experiment chose an inline dossier. +- **Pick master-detail vs. persistent-inline by item count + interactivity.** A + right-side **Sheet tray** scales to N rows and 10–30 scrolling docs and keeps + the list compact; a **persistent inline stack** wins for very few, read-only + units where hiding one behind a click is pure friction. Order Outs (~2 + read-only items) went inline; the disclosures/Mail/CC1 pattern uses the tray. +- **Make every state viewable before you polish it — you can't craft what you + can't see.** Gate rich fixtures behind a URL param, live-fetch when absent + (`?orderOutsStub=1|waived|inprogress|cancelled|notordered`, one-line hook + override). Add a distinct stub per state and verify EACH — the dev-API loan + lacked rich data, so this is the only way the full craft is visible. + +## Layout, hierarchy & density + +- **Figure-on-ground beats hairlines.** Articulate regions with one canvas color + a step off card + elevation + spacing, not dividing borders ("paper on a grey + well"). Order Outs cards sit on `bg-muted/20` with `border-border/60` + + hover-lift, not boxed dividers; the evidence viewer shares one `--bg-canvas` + and lets only chips/shadows carry contrast. +- **One focal fact per unit — the "hero fact."** Each row/card earns a single + high-weight number the eye lands on; everything else is muted support. + Appraisal card → appraised value `text-2xl tabular-nums` right-aligned, `h2` + name kept clean. +- **"Not bland with few items": pack each row with at-a-glance signal.** A + 2-item screen still reads as intentional when each row carries icon + name + + status + hero fact + a momentum cue + quiet counts — + `8 milestones · latest May 16, 2026 · 2 contacts · 2 docs` + a milestone strip + — a scannable dossier, not a thin line. +- **Don't inflate dead space for symmetry; anchor actions to their content.** + Equal-height columns top-anchor their content (never stretch); an action lives + in the header `CardAction` or `CardFooter`, never orphaned mid-card + (identity → facts → action). +- **Push long/secondary detail behind an affordance.** Filenames, notes, vendor + refs wrap and wreck a row — move them into a dialog/icon. The underwriting tab + pulled filenames into a paperclip dialog; Order Outs docs open the shared + `DocumentPreviewDialog`. +- **Columns are data — drop one that would be all-noise, keyed to the full + dataset.** A column that's all-empty/all-dash/all-same is removed, memoized + over ALL rows so layout never shifts as you filter. "A table where every row + is a dash is noise." + +## Typography & numbers + +- **Muted-first, 3-step ink scale.** Everything supporting is + `text-muted-foreground`; reserve `font-semibold`/`text-foreground` for the one + emphasis per unit. Meta lines, eyebrow headings + (`text-xs uppercase tracking-wide text-muted-foreground`), and + `CardDescription` are all muted. +- **`tabular-nums` on every number that aligns or updates — non-negotiable.** + Money, dates, counts, percentages, zoom%. The underwriting playbook flags this + as the one thing to always ADD to a money/date tracker. +- **Variable text clamps with a `title` fallback and never resizes layout.** + Two-line clamp + `overflow-wrap: anywhere`, full text on hover; long values + truncate beside a fixed control, never shove it (see the copy-button fix under + Verify). + +## Content honesty & states + +- **Blank ≠ dash ≠ value.** Not-applicable renders empty; a real-but-unscored + value renders the value (`100%`), never a placeholder dash. An in-progress + appraisal shows *no* appraised value at all (the `Fact` component returns + `null` when absent) — this single discipline is why a screen reads as data. +- **Empty ≠ error; failure is honest and distinct.** Never show a "no data" + empty state for a load failure (it reads "nothing here" and invites redundant + re-entry). Partial vs. total failure read differently ("Couldn't load 10" vs. + "8 of 10 loaded"). Order Outs uses an inline `Alert` + Retry, separate from the + `EmptyState`. +- **Never fake a number or a progress script.** Narrate real observed backend + state; fall back to a value you actually have rather than a dash (on a vendor + detail failure, fall back to the summary's direct-order value so a real value + never reads as "—"). +- **Model mutually-exclusive states as first-class either/or.** An appraisal is + EITHER ordered OR waived — the waived case is a resolved positive hero (green + seal, "Appraisal waived — no order needed"), never a sad empty state leaking + `Program: PROPERTY_INSPECTION_WAIVER` and never a "Place order" affordance. +- **Humanize enums at the boundary; never leak wire values to the UI.** + `TITLE_COMPANY` → "Title company"; the waiver program mapped via an inline + Title-Case map in the model — not the copy catalog, and never a vendor name + (see the MEMORY note on vendor names). +- **Cover the whole state space and make each state demoable.** Audit every + status (ordered / in-progress / terminal-negative / not-ordered / waived + + title equivalents + empty contacts/docs); a terminal-negative reads "stopped," + not "broken." Gate each behind a stub mode with a spec. + +## Status signaling + +- **Status = shape + word + color, never color alone.** Back every tone with a + distinct glyph + a text label from one central resolver + one label catalog. + "open/not-yet" is a dashed empty outline dot (`border-dashed bg-transparent`), + not a hue; a distinct failed glyph serves color-blind users; every dot is + `role="img"` + `aria-label` + `title`; `StatusDot` + `Badge` always paired. + +## Motion + +- **Microanimations are functional and lawful, each with a reduced-motion + off-switch.** Two easings, a 100/200/300ms duration law (longer = an + explicitly-flagged brand exemption); nothing pops (width/opacity ease in AND + out). Order Outs — tray slide, staggered milestone reveal, row hover-lift, + chevron rotate on select, copy-button icon swap, section fade-in — all wrapped + in `motion-safe:` / `motion-reduce:transition-none`; the repo lint enforces the + fallback. +- **Cap staggered entrances so long lists stay calm.** Cap the per-item delay + (e.g. at the 10th child) so a 50-row list doesn't cascade for seconds; key the + container on the active view to replay the entrance on switch. + +## Structure, semantics & a11y — the HTML is a deliverable + +- **Reconstruct the rendered DOM and justify every surviving `
`.** Semantic + elements over roles: `