Turn a requirement or an unproven technique into a single self-contained HTML prototype that doubles as a design spike, rebuilt at high fidelity from the app's own source
77
97%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
One file, two panes: a reviewer shell on the left and a device stage on the right. The shell is invariant across prototypes and is the only part you copy. The app chrome inside the stage is composed fresh every time from the harvest table — never templated, because a reused app frame is exactly how the wrong tokens and invented labels get in.
<div class="wrap">
<header class="page"> eyebrow (anchor id) · h1 (the question) · lede
<div class="layout">
<aside class="panel"> controls · service parameters · "where you are" · restart
<div class="stage"> the device frame + a caption underneath.layout is grid-template-columns: minmax(0, 320px) minmax(0, 1fr), collapsing to one column
below ~900px..stage is position: sticky; top: 24px on wide viewports, static when collapsed.height: min(760px, calc(100dvh - 130px)); min-height: 520px. Use 100dvh, not 100vh — mobile
browser chrome makes vh lie.Two disjoint token sets in :root, and the separation is load-bearing: it is what stops a client
mistaking reviewer chrome for product.
The values below are fictional, from the same invented app as the harvest example — the point is the three-group structure and the citation format, never the specific colors or line numbers.
:root {
/* shell — reviewer chrome, deliberately NOT the app's design language */
--shell-bg: #EDF0EE;
--shell-panel: #FFFFFF;
--shell-ink: #1E2622;
--shell-ink-soft: #5F6B64;
--shell-line: #D2D8D4;
--shell-accent: #3F7D4E; /* the app's brand color, used ONLY for shell focus/accent */
--shell-shadow: 0 24px 60px rgba(30, 38, 34, .18);
/* app — harvested, each line carries its source */
--brand: #3F7D4E; /* src/styles/palette.ts:24 */
--ink: #22302A; /* src/styles/palette.ts:26 */
/* prototype-only — justified */
--annotation-accent: #B5179E; /* annotation layer only; MUST NOT exist in the app, so it can never be
mistaken for product UI */
}Theme handling for the shell, three blocks, in this order — a color whose only definition sits inside a media query has no light-mode value at all:
:root { /* complete light palette */ }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { /* overrides */ } }
:root[data-theme="dark"] { /* same overrides, so an explicit toggle wins */ }The app tokens do not participate. If the app is light-only, say so in a comment and cite the
resolver line that proves it — a theme function with an early return false is a fact you read, not
an assumption you made.
Screens are sections toggled by a class. Exactly one function changes screens, and it updates every dependent surface in the same call — split that logic and the step list will drift out of sync with the frame within two edits.
function show(name) {
clearTimers(); // 1. cancel in-flight polling
current = name;
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.querySelector('#s-' + name).classList.add('active');
document.querySelectorAll('[data-nav] button').forEach(b => // 2. nav highlight
b.classList.toggle('on', b.dataset.go === name));
document.querySelectorAll('#steps li').forEach(li => // 3. step list
li.classList.toggle('on', li.dataset.s === name));
setFlowName(name); // 4. flow label
caption(captionFor(name)); // 5. narration
}clearTimers() first is not incidental: leaving a poll running after the user navigates away is both
a bug and a silent contradiction of any "leaving cancels the wait" criterion.
Under the frame, one line that changes with every state and says what happened and why:
Never leave a stale caption after a screen change or a branch of the data loop.
A body class plus one prototype-only color. Everything the requirement adds, changes, or removes is reachable through it, and nothing else is.
.tag { display: none; }
body.annot .tag { display: inline-block; }
body.annot .is-new { outline: 2px dashed var(--annotation-accent); outline-offset: -2px; }Use outline, not border — a border changes layout and the annotated screen stops matching the
unannotated one.
If the prototype displays a document produced elsewhere, reproduce the app's isolation posture verbatim — the sandbox token list, and the choice of inline content versus a URL. Both are security decisions with reasons; a prototype that relaxes them proves nothing about the real screen.
Not optional, and each one gets exercised during the drive:
:focus-visible { outline: 2px solid var(--shell-accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .spin { animation-duration: 3s; } }Plus: real <button> elements for anything clickable, h1 for the question, text-wrap: balance on
the headline, and font-variant-numeric: tabular-nums wherever the app shows codes or money.