Neo Kinpaku design system + Live Mode v3 (#169)

* Add neo kinpaku design system page

* skill: rip out baked-in category recipes and saturated-default motion tropes

Programmatic bias mining (impeccable-evals) traced four major defects
back to specific lines in this skill that contradicted SKILL.md's own
first-order-reflex warning:

- brand.md "Pairing and voice" prescribed four category→aesthetic
  recipes (editorial → serif+sans, tech/dev/fintech → tight tracking,
  consumer/food/travel → script/display serif, creative → rule-break).
  These directly drove OpenAI's 76% extreme-negative letter-spacing
  on tech briefs and Anthropic/Google's 28-34% italic-serif-display
  slop on editorial/food briefs. Replaced with one sentence: the
  shape depends on the brand, not on the brand's category.
- brand.md "Brand permissions" had "Typographic risk. Enormous
  display type, unexpected italic cuts, mixed cases, hand-drawn
  headlines, a single oversize word as a hero." — a four-for-one
  slop driver behind 97% OpenAI comically-large H1, 42% bad-SVG
  illustration, and the editorial-italic slop. Deleted outright.
- typeset.md and teach.md repeated the same category recipes;
  trimmed to the principle without the recipe.
- SKILL.md Typography: added a hard hero-H1 ceiling (clamp() max
  ≤ 6rem ≈ 96px), with a <codex> block to make it explicit since
  OpenAI over-indexes here (97% ≥128px vs 24% for Anthropic).
- animate.md, bolder.md, brand.md: removed "staggered reveals" and
  "scroll-triggered transitions" as the prescribed default ambitious
  motion. By 2026 that's the saturated AI tell, not a choreography.
  Reserved stagger for legitimate list-sibling rhythm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: anti-cream + codex-specific defect bans + universal slop bans

Second pass after measuring more biases against the eval corpus.

- SKILL.md Color: explicit "cream/sand/beige body bg is the saturated
  AI default of 2026" rule. Tone down the "tint every neutral" line so
  it doesn't read as "default to warm-tinted near-white" (which OpenAI
  hits at 74% and Anthropic at 31%-47%).
- SKILL.md Absolute bans: add universal bans for two slop patterns
  detected at 55-95% across providers — tiny uppercase tracked eyebrow
  above every section (the 2023-era kicker that's now AI grammar) and
  numbered section markers (01/02/03). Also explicit "text that
  overflows its container is the universal defect on tablet/mobile."
- SKILL.md Absolute bans → <codex> block: ban the GPT-specific defects
  Paul annotated repeatedly — `border:1px solid` + soft-wide-shadow
  (≥16px blur) "ghost cards", `border-radius:32px+` over-rounding,
  hand-drawn/sketchy SVG illustrations (loose-sketch / *-sketch classes,
  feTurbulence paper-grain filters), repeating-linear-gradient stripes,
  "X theater" AI-slop copy phrases.
- SKILL.md Motion → <gemini> block: the image :hover transform tell
  (38% Google skill-on rate). Hover effects on images add no info; the
  image isn't an action target. Animate card chrome, not the image.
- SKILL.md Typography: hard display letter-spacing floor ≥-0.04em
  (OpenAI defaults to -0.075em → cramped). Existing hero ceiling
  <codex> block extended with the letter-spacing rule.
- codex.md Step A example: stop seeding "warm-grounded (deep oxblood +
  cream)" as the warm-palette template, which primes the cream default.
- colorize.md Tinted backgrounds: stop printing the literal cream
  recipe `oklch(97% 0.01 60)`; replace with brand-anchored guidance.
- document.md examples: warm-ash-cream → cool-paper so the example
  doesn't seed cream as the canonical neutral example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: universal anti-slop bans + contrast/font-count/all-caps-body rules

Third pass after measuring the rest of the cross-provider matrix:

- Color: explicit "Verify contrast" rule. Low-contrast text fires at
  68% across all providers skill-on (90+% off). The most common
  failure is muted gray body on a tinted near-white; light-gray-for-
  elegance is named as the single biggest cause of unreadable AI
  pages.
- Typography: max-3-font-families rule. Overused-fonts (>4 families)
  fires at 28% Anthropic / 36% Google / 0% OpenAI skill-on; >50% off.
  Also: universal "no all-caps body copy" (moved from brand-only ban
  to Shared design laws since product-register also overuses caps).
- Copy: anti-aphoristic-cadence ban targets Anthropic's signature
  "X. No Y." / "X. Just Y." voice (63% skill-on copy-slop rate, 77%
  off — the worst rate in the matrix). Once-is-voice / three-or-more-
  is-tell framing per the runner's copy-slop detector.
- Copy: anti-SaaS-buzzword-string ban with the literal phrase list
  the detector watches for (streamline/empower/supercharge, trusted-
  by-leading, best-in-class/enterprise-grade/cutting-edge, etc).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: strengthen anti-cream rule across full warm-neutral band

Smoke validation showed the cream fix worked for Google + OpenAI but
Anthropic Sonnet italian-restaurant still shipped `--paper: oklch(90%
.018 88)` — cream just outside the L≥95% band the rule cited.

Broaden the rule:
- Band: OKLCH L 0.84-0.97, C < 0.06, hue 40-100 (was 95-97% / 60-95).
- Name the token-name tells explicitly (paper / cream / sand / bone /
  flour / linen / parchment / wheat / biscuit / ivory) — the model
  defaults to one of these regardless of what hex it lands on.
- Call out the specific brief patterns ("warm, traditional, family-
  coastal-Italian" / "editorial-restraint") that the model translates
  into cream by reflex. Then provide three explicit non-cream options:
  saturated brand color, true off-white at C=0, or darker mid-tone.

Warmth in the brand is carried by accent + typography + imagery, not
by body bg.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* v3.2.0: skill bias-fix release

Bumps version from 3.1.1 to mark the four-commit skill cleanup that
rips out baked-in category recipes (brand.md), saturated-default motion
tropes (staggered reveals everywhere), the cream/sand body-bg AI tell,
codex-specific defects (1px+wide-shadow, over-rounding, hand-drawn SVGs,
stripes, X-theater copy), the extreme-letter-spacing default, and
universal slop bans (all-caps eyebrow on every section, numbered-section
markers, all-caps body, font-family-count > 3, aphoristic copy cadence,
SaaS buzzword strings). Plus a hard hero-H1 ceiling (clamp() ≤6rem) and
a Gemini-specific image:hover transform block.

Validated against ~190 post-fix samples — see impeccable-evals
biases tab for per-provider deltas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* drop "no pure black/white" rule entirely

The rule was contested in the design world and causing more damage than
good — pushing every page into the tinted-near-white default which is
the cream/sand AI tell we already explicitly ban elsewhere. Vercel,
SVKMS, Brutalist sites, et al. use pure black/white successfully; the
skill shouldn't second-guess that.

Skill markdown deletions:
- SKILL.md Color: drop the "Never use #000 or #fff" bullet.
- color-and-contrast.md: drop the "Never Use Pure Gray or Pure Black"
  subsection, the "Never pure black" table-row prescription, and the
  "Avoid: Using pure black for large areas" bullet.
- colorize.md: drop the "NEVER use pure black or pure white for large
  areas" bullet.
- polish.md: drop the "Tinted neutrals: No pure gray or pure black"
  half of the bullet (the gray-on-color bullet survives).

Detector code (cli/engine):
- registry/antipatterns.mjs: remove the `pure-black-white` entry.
- rules/checks.mjs: remove the three `findings.push({ id:
  'pure-black-white', ... })` emit points (inline #000 bg, Tailwind
  bg-black class, plain-HTML scan path).
- engines/regex/detect-text.mjs: remove the two pure-black-white regex
  rules (CSS `background: #000…` + Tailwind `bg-black`).
- detect-antipatterns-browser.js: regenerated via
  scripts/build-browser-detector.js.

Tests:
- detect-antipatterns-fixtures.test.mjs: invert the assertion that
  pure-black-white fires; expect it to NOT fire post-v3.2. Drop the
  Tailwind bg-black-opacity edge-case test (no longer relevant).
- detect-antipatterns.test.js: drop the standalone "detects pure-
  black-white in styled-components" test and remove pure-black-white
  from the multi-detector assertions in PricingCard, globals.css, and
  GlobalStyle.tsx tests.

166 bun tests pass; 24 node fixture tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: strip example patterns from copy rules, strengthen gemini block

v3.2 rerun validation surfaced two issues:

1. Copy-slop detector fires more on Gemini under v3.2 (48% → 84%) than
   under no-skill baseline. Root cause: the anti-aphoristic-cadence rule
   printed the literal "X. No Y." / "X. Just Y." patterns as examples,
   and Gemini imitated them as the recommended voice. Same recipe-becomes-
   bias trap we hit with brand.md:116's "Enormous display type, unexpected
   italic cuts, mixed cases, hand-drawn headlines" enumeration. Fix:
   describe the cadence as a rhythm ("serious statement, then punchy
   short negation") without printing literal patterns. Buzzword list
   trimmed to a single inline phrase family rather than quoted strings.

2. Gemini image:hover transform Gemini-tell hadn't dropped (31% off →
   32% v3.2). Strengthen the <gemini> block: explicit "Never animate
   <img> elements on hover", call out the Tailwind group-hover:scale /
   group-hover:rotate / group-hover:translate parent-hover patterns by
   name (Gemini was reaching for these via Tailwind even though the
   prior text talked about :hover on the image directly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: simplify context loading and inline register directive

Replaces load-context.mjs's JSON output with a tight markdown block from
the renamed context.mjs. The script now extracts PRODUCT.md's `## Register`
field and appends a `NEXT STEP:` directive naming the matching reference
(brand.md / product.md), which moved Gemini from skipping the register
load entirely to honoring it. Drops the `.impeccable.md` auto-migration;
makes IMPECCABLE_CONTEXT_DIR a lazy escape hatch consulted only when the
default paths come up empty.

Setup is now four bullets in one list. The DESIGN.md nudge is gone; in
its place, a "familiarize with the existing design system" step that
calls out CSS / tokens / running app as authoritative sources alongside
DESIGN.md. The standalone `### Register` H3 stays for the cascade rules
(task cue → surface → register field).

New LLM-backed test suite at tests/skill-behavior/ runs five scenarios
against claude-haiku-4-5, gpt-5.4-mini, and gemini-3.1-flash-lite via
Vercel AI SDK. Captures real tool traces, asserts on context.mjs calls,
brand.md loads, and teach.md fallback. Skips cleanly when API keys are
unset. 13-14/15 pass; only stable failure is the v3.2.0-era gpt-mini S4
"don't re-run" regression. Adds @ai-sdk/google as devDep and the
test:skill-behavior npm script.

Touches em-dashes in skill/SKILL.md and four reference files so
`bun run build:skills` passes its skill-prose validator. teach.md and
document.md drop their "re-run the loader to refresh session cache"
steps since the agent's own write is now the freshest source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: merge orphan reference files into command sub-skills + inline S-tier invariants

Two related restructurings:

1. SKILL.md now carries the cross-domain invariants that catch defects in any
   project (contrast/placeholder/gray-on-color, similar-font pairing, text-wrap,
   tabular-nums, centered-stack default, Flex/Grid choice, auto-fit grids,
   semantic z-index, reduced motion, stagger vs section-fade, premium motion
   materials, focus-visible, placeholders-aren't-labels, dropdown overflow trap,
   button/link copy). Greenfield-only rules (theme picking, color strategy,
   tinted neutrals) live under "New projects only".

2. Reference files merged into their command counterparts:
   - spatial-design.md  -> layout.md
   - motion-design.md   -> animate.md
   - color-and-contrast.md -> colorize.md
   - responsive-design.md  -> adapt.md
   - ux-writing.md         -> clarify.md
   - typography.md         -> typeset.md (bolder.md redirected)
   - cognitive-load.md + heuristics-scoring.md + personas.md -> critique.md

   craft.md and shape.md "load references" lists updated to new file homes.
   interaction-design.md stays standalone (no 1:1 command verb).

Net: 36 -> 27 reference files. Same content, fewer files, no orphaned
reference loaded only from craft.md.

Also extends the routing rules: if the user's first word doesn't match a
command but the intent clearly maps to one, load that command's reference
and proceed as if invoked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: add sub-command + existing-project scenarios; move sub-command load to step 2

Adds three new LLM-backed scenarios to tests/skill-behavior:
- S6: `/impeccable polish` → loads polish.md
- S7: `/impeccable audit` → loads audit.md
- S8: existing SvelteKit project (PRODUCT.md + DESIGN.md + src/app.css +
  src/lib/components/*.svelte + src/routes/+page.svelte) → agent reads
  at least one project code file to understand the existing design system

S6/S7 surface a real model-floor: gpt-5.4-mini reads brand.md, reads the
target index.html, and just does the polish/audit without ever loading
the sub-command reference. Stronger SKILL.md wording didn't move it.
Captured in the README baseline as a known weakness. Claude and Gemini
honor the load reliably.

To fix Gemini on S6/S7, sub-command reference loading is now Setup step 2
(right after context.mjs), not step 4 — placing it before the model gets
focused on "doing the work". Step 3 (design-system familiarization) is
tightened to require at least one project code read even when a
sub-command reference loads in step 2, so Claude doesn't laser-focus on
the sub-command flow and skip the broader exploration.

Two new fixtures: MINIMAL_LANDING_HTML (a tiny static landing page for
S6/S7) and SVELTE_PROJECT_FILES (a minimal SvelteKit scaffold with
tokens, components, and a routes/+page.svelte for S8). Both designed to
look real enough that agents treat them as production code.

Suite is now 24 tests across three providers; baseline is 21-22/24, with
the stable failures being gpt-5.4-mini scenarios 6 and 7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: add reveal-animation safety rule (must enhance, not gate visibility)

Class-triggered visibility transitions pause on hidden tabs and headless
renderers. The italian-restaurant smoke produced a build where 2 sections
shipped opacity:0 because the CSS transition never advanced past
currentTime=0 (timeline paused). Added one-liner under Motion to prevent
the antipattern: reveals must enhance an already-visible default, never
gate content visibility on a class-triggered transition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: restore prescriptive cream/sand/beige paragraph

Bisection across 5 historical skill commits on Gemini 3.5 flash fast
lane n=3 found that 0cf2debd was the peak quality state. The regression
between 0cf2debd and HEAD came from simplifying the long anti-cream
paragraph into a one-liner.

Restoring the paragraph (with em-dashes replaced by parens to satisfy
prose lint) recovers ~0.22pt average on Gemini vs HEAD, with the
largest gains on:
- 09-luxury-hotel: +0.50 (restores editorial drama in photo-led briefs)
- 10-food-magazine: +0.67
- 03-italian-restaurant: +0.51

The paragraph's load-bearing parts are the (a)(b)(c) alternatives that
give the model actionable replacements for cream-tinted body bg
("saturated brand color as body", "true off-white at chroma 0",
"darker mid-tone tinted neutral"). Without them, the one-line warning
left the model with no concrete alternative.

Cross-provider validation showed the pattern matches historical
behavior: Gemini benefits from prescriptive scaffold (+0.12 over off),
Sonnet is roughly neutral (+0.01), GPT-5.5 slightly regresses (-0.11
matching the v3.1.0 pattern of -0.11). The skill has never been
uniformly better than skill-off across providers; this is the closest
achievable state without provider-specific rework.

The structural improvements from the prior restructure stay (file
merges, S-tier inlines, routing rule extension, reveal-animation
safety rule).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: teach CLAUDE.md / AGENTS.md / DEVELOP.md about the skill-behavior tests

Adds the `bun run test:skill-behavior` script to the test commands lists
in all three docs. CLAUDE.md gets a full `### Skill-behavior tests`
subsection paralleling the existing Live-mode E2E one: how the suite
works (inlines source SKILL.md, scoped tools, asserts on the trace),
which providers it always runs (claude-haiku-4-5, gpt-5.4-mini,
gemini-3.1-flash-lite — all three every run), the eight scenarios, the
baseline (21-22/24 with stable gpt-mini sub-command-routing failures),
auth via repo-root `.env`, and how to add a scenario.

AGENTS.md gets the one-liner plus a paragraph in Testing Guidelines that
points contributors at the suite for Setup-touching edits (SKILL.md
Setup section, context.mjs, teach.md, document.md, register / sub-command
refs).

DEVELOP.md gets a short Testing section that didn't exist before, plus a
nudge in the "Test across providers" bullet pointing at the new suite as
the automated way to do that.

No code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* detector: add 5 new antipatterns (em-dash-overuse, broken-image, marketing-buzzword, numbered-section-markers, aphoristic-cadence)

Consolidates eval-side detection logic into the canonical impeccable
detector. Before this change, the eval harness had its own duplicate
implementations of em-dash, copy-slop, and broken-image checks. They
now live alongside the existing 28 antipatterns in the impeccable
registry, available to the CLI, browser extension, critique skill,
and eval (via the existing slop grader child-process call).

New antipatterns:
- em-dash-overuse: 5+ em-dashes in body text content (threshold
  permits legitimate prose use of em-dash; only triggers on AI
  cadence-level density)
- broken-image: <img> with empty src, missing src, or src="#"
- marketing-buzzword: SaaS phrase list (streamline / empower /
  supercharge / enterprise-grade / cutting-edge / etc)
- numbered-section-markers: repeated 01 / 02 / 03 sequence as
  section labels — the AI editorial scaffold one tier deeper than
  tracked eyebrow chips
- aphoristic-cadence: 3+ manufactured-contrast ("Not a X. A Y.")
  or short-rebuttal ("Sentence. No clause." / "Sentence. Just
  clause.") constructions in body text

Engine wiring:
- broken-image runs as a static-html element rule (selector: img)
  and a fallback regex matcher (for non-HTML files)
- em-dash / buzzword / numbered / aphoristic run as regex
  page-analyzers, factored into a new runTextContentAnalyzers()
  helper that both detectText (non-HTML) and detectHtml (HTML)
  call, so .html files get the same coverage as .css/.tsx

Tests: 166 detector + 12 browser + 24 fixture all pass.
Browser detector rebuilt (162.7 KB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: drop unvalidated anti-centering rule; add image-led hero carve-out

The anti-centering rule ("Don't default to centering everything") was
added without empirical support. We have a detector for it
(everything-centered, threshold ≥70%) that fires on 0 / 998 samples
in the corpus — never validated, never useful.

Meanwhile the rule was almost certainly responsible for collapsing
Gemini 3.5 flash's luxury-hotel skill-on output from the canonical
"full-bleed photo + centered overlay headline" cinematic hero (the
shape skill-off Gemini chooses 67% of the time) to a 50/50
magazine grid (full-bleed rate drops to 18% under skill-on, -49pp).

Changes:
- skill/SKILL.md #### Layout: drop "Don't default to centering..."
- skill/reference/brand.md ## Layout: drop the same rule; replace
  with a positive carve-out — image-led briefs (hotels, restaurants,
  magazines, photography) often want full-bleed hero with overlaid
  menu and centered headline; let the photograph be the design
- skill/reference/layout.md: drop the assessment question and the
  "asymmetric breaks centered-content pattern" framing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Apply neo-kinpaku design system and improve live picker UX

Restyle the live picker to match the site kinpaku kit, persist pick mode
in localStorage, fix DESIGN.md color swatches in the parser, and land the
neo-kinpaku site refresh with new tokens, assets, palette script, and
detector rules.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add live Steer end-to-end: poll protocol, browser UI, and E2E harness.

Wire page-level Steer through the live server and agent poll loop with steer_done
unlock semantics, extend live.md for agents, and add smoke tests with LLM
handleSteer plus recovery for hidden heroes, HMR lag, and dev-tool overlays.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add experimental live-poll --stream mode; keep one-shot default for Cursor.

Stream keeps one process alive with ack-aware resume, but live.md documents
that Cursor should stay on one-shot background notify after testing showed
~5s pickup vs sub-second on exit-based notify.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Sync harness output and fix build validators for poll stream release.

Regenerate provider skills after live-poll --stream work, update homepage
detection counts to 41, and replace em dashes in site/skill copy so
bun run build passes prose and count checks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* homepage: add testimonials marquee section

A two-row testimonial marquee on a tinted graphite plinth, sitting
between the hero and the slop teaser.

29 testimonials sourced via api.fxtwitter.com (lightly cleaned: leading
@-mention reply targets stripped, trailing self-links removed). Avatars
downloaded into site/public/assets/testimonials/ so they're served
locally. Quote order curated for impact — both rows lead with the
punchiest quotes (Ben Davis spotlight, "Impeccable > Claude design",
"THIS. This shit works.", "Uninstall whatever frontend skill you're
using.") so the first viewport is loaded with the most memorable
testimonials.

Engineering notes:
- Section uses width:100vw + margin-left:calc(50% - 50vw) to escape
  main.site-content's max-width + side padding (cards now clip cleanly
  at the actual viewport edges).
- Marquee runs at 110s linear infinite. Both rows share the same
  duration so on-screen speeds match; track is doubled so the loop
  back to 0 reads as continuous.
- Hero min-height reduced from 100svh to calc(100svh - 115px) so the
  dotted divider and top of row A peek above the fold on landing,
  signalling the section is there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* homepage: keep the hero demo clear of the fixed header on short viewports

The hero centers its content in the full viewport (the site header is a fixed
overlay), so on shorter screens the tall Live Mode demo tucked under the nav.
Raise the hero's top padding above the 97px header (113px wide, 108/92px when
stacked) so content always pins below the header while still centering on tall
viewports, and cap the demo frame to the viewport so the whole demo stays on
screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add steer voice input and refine processing animation.

Wire Web Speech API on the Steer mic with auto-submit, block Cursor's preview browser with a clear message, and replace truncated "Working" text with a dots-only processing state.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add agent poll connectivity indicator and tighten global bar spacing.

Surface poller state on the Impeccable mark via SSE and /status, with an instant disconnected tooltip, steer timeout failsafe, and matched brand/chat section gaps.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix steer focus to allow page text selection without losing type-to-steer.

Blur the hidden steer input on page interaction, pause refocus during selection gestures, and reschedule focus recovery after clicks and cleared selections.

Co-authored-by: Cursor <cursoragent@cursor.com>

* site: rework "Design in production" section glyphs and audience band

Put the three how-it-works steps back into thin-line cards and drop the
overused browser-chrome bars from each glyph. Redraw the step 2 and 3
visuals to mirror the real Live Mode UI: step 2 shows the on-canvas pick
outline with an attached comment bubble, step 3 shows the floating
contextual accept bar plus the source-write confirmation. Re-treat the
audience tiles as verdigris-lined text (no card box) under a "Who it's
for" eyebrow, so each role reads as distinct from the gold step band.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add live insert mode with HMR-safe placeholder recovery.

Ships insert picking, scaffold helpers, variant cycling fixes for hidden
variants, and placeholder snapshot/recreation so Astro HMR does not drop
the wait-state box or re-anchor to the hero container.

Co-authored-by: Cursor <cursoragent@cursor.com>

* site: mobile pass — hamburger nav + designing hero overflow fix

The header was rendering inline nav links + GitHub button that overflowed
narrow viewports (~363px). Pre-existing display:none hacks hid Designing
and Live to make the row fit, but those items still belonged in the menu.

Header.astro: added a hamburger toggle button + inline script. The right
cluster (nav + GitHub) becomes a collapsible drawer below the header on
mobile, with data-nav-open driving the open/closed state and animating
the two-line glyph into an X.

kinpaku-kit.css: hamburger button (kinpaku-bordered glyph), mobile drawer
panel (solid lacquer-deep bg, hairline separators between rows, full-width
tappable rows), and overrides for the older sub-pages.css mobile rules
(horizontal-scroll mask on the nav, hidden [data-nav="home"] item, hidden
GitHub star label) — all redundant now that the drawer surfaces everything.

home-kinpaku.css: dropped the @media (max-width: 560px) block that hid
Designing / Live / GitHub. The drawer pattern shows them all.

designing-kinpaku.css: hero h1 "Designing with Impeccable" was overflowing
at narrow viewports. Three fixes:
  - grid-template-columns 1fr → minmax(0, 1fr) so the column shrinks to
    fit container instead of growing to "Impeccable"'s 472px intrinsic
    min-content width.
  - mobile h1 size override (clamp(2.2rem, 11vw, 3rem) at <=480px) since
    the display token's 3.4rem minimum is sized for desktop hero impact.
  - hide the decorative loop-wheel SVG below 600px (was overflowing 22px
    past the right edge).

Verified clean at both 363px and 403px viewports across /, /docs,
/docs/animate, /slop, /designing, /live-mode. scrollWidth matches viewport
width on every page (no horizontal scroll).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* detector: refine new rules + run provider tells in browser env

Follow-up to the detector port (rules landed in 7648af00):
- oversized-h1: flag long headlines set at display size, not punchy
  one/two-word heroes (length, not size alone, is the tell)
- provider tells (--gpt/--gemini) now always run in a real browser env
  (detector page, live overlay, extension); gating is a CLI-output
  concern only, applied in the Node engine return paths
- move theater-slop-phrase into checkHtmlPatterns so it runs in the
  bundled browser path, not just CLI/static (browser bundle excludes
  detect-text.mjs)
- hero-eyebrow-chip overlay highlights the eyebrow, not the heading
- gemini-tells fixture: data-URI images so the hover-zoom renders
- rebuild browser bundle

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: migrate /detector lab to neo-kinpaku design system

Rebuild the detector lab tool shell on --ks-* tokens (lacquer ground,
gold hairlines, champagne/mono type) instead of the legacy warm-paper
palette. Swap the "/" placeholder for the real carved-tile brand lockup,
restyle the toolbar actions as kinpaku primary/secondary buttons, and
recolor the finding overlay from off-brand magenta to vermilion.

Update the global theme-color from #fafafa to #010101 (the sRGB render
of the lacquer ground) so the browser chrome matches the dark site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Homepage: hero finalist, compact live demo, real picker bar.

Switch the hero to m-01-v2-01, tighten the in-hero demo layout, and replace
the marketing gbar with a shared LiveDemoGbar that mirrors live-browser.js.
Size the bar with max-content so controls are not clipped inside the capsule.

Co-authored-by: Cursor <cursoragent@cursor.com>

* site: migrate /cases/neo-mirai to neo-kinpaku design system

Rebuild the Neo Mirai case-study page on --ks-* tokens: lacquer ground
(drops the off-brand magenta radial spotlight), Alumni Sans Pinstripe
display headings instead of the banned italic serif, gold eyebrow/labels,
gold hairline image frames, kinpaku primary/secondary buttons, and a
lacquer-deep command panel with a gold-bordered code block.

Opt .neon-case-page into the shared kinpaku site-header/footer chrome in
kinpaku-kit.css (per the "add new kinpaku pages to the selector list"
note) so the global header and footer go dark to match the page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: consolidate kinpaku header+footer into one reusable .kinpaku-chrome class

The dark header/footer were not a reusable unit: the header was scoped to
a per-page selector list, the github star pill was home-only, and the
default footer was copy-pasted into four page stylesheets. Pages not on
the lists (like /cases/neo-mirai) fell back to the legacy light chrome.

Collapse all of it into one `.kinpaku-chrome` block in kinpaku-kit.css —
header, github pill, and default footer — and opt every kinpaku page in
via a single body class. Delete the four duplicated per-page footer
blocks and the home-only github pill. The home page keeps its textured
verdigris footer as a deliberate override, raised to body.home-kinpaku
specificity so it wins regardless of import order. Genuinely light pages
(privacy, tutorials) just omit the class.

Fixes on /cases/neo-mirai: footer and github star now render dark/kinpaku
(were legacy-light), and the content sections are wrapped in the .neon-case
container so they sit in header-aligned gutters instead of bleeding to the
viewport edge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: migrate privacy + tutorials to kinpaku via a reusable surface class

These were the last two light pages. Rather than rewrite their per-rule
styling, add a reusable .kinpaku-surface class that remaps the legacy
--color-* / --font-* tokens to kinpaku values at the body scope, so the
existing legacy-token CSS (sub-pages.css prose, the pages' inline styles)
renders dark for free. Same trick docs-kinpaku/slop-kinpaku use per page,
lifted into one shared class. Pair it with .kinpaku-chrome for header +
footer.

privacy + both tutorials pages now carry both classes. Also force the
sub-1.2rem headings (tutorial card titles, prose h1/h2) back to the
upright body face: the legacy display face was italic serif, and the
kinpaku Pinstripe face reads wrong synthesized-italic at small sizes.

No light pages remain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: re-add Tutorials to the /docs sidebar

Tutorials lost its docs placement across two refactors: the Astro docs
rebuild never carried over the sidebar tutorials list the old generated
pages had, and the kinpaku homepage redesign dropped the "Full
walkthrough" link. It survived only via /designing and /live-mode.

Add a "Tutorials" group at the top of the docs sidebar (matching the
command-category styling) linking the index plus all four tutorials,
restoring the old information architecture.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: make kinpaku the default — flip legacy :root tokens to dark (phase 1)

Repoint the legacy design tokens in tokens.css from light-mode to kinpaku:
--font-* now reference the --ks-* brand faces (retiring Cormorant/Instrument/
Space Grotesk), surfaces carry dark-lacquer oklch, and --color-accent is gold
instead of magenta. Values mirror the per-page kinpaku remaps.

Every live page already overrides these at its body-class scope, so this
changes the fallback (any classless/new page now renders kinpaku) without
altering existing pages — verified home, designing, slop, live-mode, docs
unchanged, and the deliberate-light demos (slop specimens, home's Aurelia
mock) still render light via their own colors.

First step toward removing the per-page remaps; those become redundant next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* detector + slop: cream-palette rule, drop everything-centered, polish catalog

- new deterministic cream-palette rule ("claude beige"): flags warm
  lightly-tinted off-white page backgrounds; wired into static + browser
  engines, with fixture + test
- remove everything-centered rule entirely (no longer in the skill) from
  registry, regex analyzer (+ index-offset fix), checkPageLayout, and tests
- catch Instrument Serif in overused-font (regex + OVERUSED_FONTS)
- /slop: reconcile catalog (cream card in, everything-centered out; counts),
  and fix demo visuals — visible hairline border, gigantic clipped hero,
  more extreme crushed tracking, padded gray-on-color card, uniform-rhythm
  monotonous-spacing, long line-length line, elastic-overshoot dialog for
  bounce easing, real zooming image for image-hover; flip the demo surface
  off warm beige to a cool neutral

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* detector page: add cream-palette fixture to the catalog

Surfaces the new cream/beige palette rule on /detector alongside the
other Color specimens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: shared docs sidebar + tutorial pages join the layout

Extract the /docs section sidebar into a reusable DocsSidebar component
and wire it into all three entry points so the navigation is consistent
across docs index, command pages, and tutorial pages.

site/components/DocsSidebar.astro (new): one source of truth. Loads the
tutorials + skills collections, renders Tutorials → Commands grouped by
category, and highlights the active entry via activeCommand / activeTutorial
props.

site/pages/docs/index.astro: swap the inline sidebar markup for the
component. Drop the "All tutorials" link — the dedicated tutorials
listing page wasn't earning its slot in the rail.

site/layouts/Doc.astro: same swap. Command pages now also see the
Tutorials section above Commands, matching /docs.

site/pages/tutorials/[...slug].astro: rewrite from a standalone page
(custom .tutorial-page wrapper, ad-hoc breadcrumb) to the full
skills-layout shell with DocsSidebar in the left rail. Tutorial content
now reads in the same layout as command reference pages.

site/content/tutorials/brand-vs-product.md (deleted): the skill picks
the register automatically from PRODUCT.md, so a tutorial telling users
to pick it themselves was misleading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* detector: catch Tailwind warm-light bg utilities in cream-palette

The static engine can't resolve Tailwind classes to computed CSS, so a
`bg-amber-50` on <body> slipped past the cream-palette rule. Add a
class-list fallback that scans body/html for arbitrary `bg-[...]` values
and named warm-light utilities (amber/orange/yellow/stone), each run
through the same isCreamColor test so neutrals and over-saturated shades
drop out. Fixture + test for the class-only case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: drop redundant per-page token remaps (phase 2)

With kinpaku now the :root default, the --color-* / --font-* remap blocks
in docs/slop/designing/live-mode-kinpaku.css re-declared values identical
to :root. Removed them, keeping only the --ks-muted alias (still read by
name in those files) and each page's shell (gradient bg, color, min-height).

home-kinpaku.css keeps its remap: it uses home-specific values (e.g.
--color-charcoal: var(--ks-text), --color-cream: var(--ks-lacquer-raised))
plus the --cat-* gradient overrides, so it is not redundant.

Verified designing (PRODUCT.md viz), slop (specimens stay light), docs,
live-mode unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: drop italic from 15 dead editorial-serif heading rules

Audited every font-style: italic in sub-pages.css and main.css against
the live markup. Removed italic from the 15 rules whose selectors don't
appear in any page/component/content/script:

  sub-pages.css: docs-home-card-title, docs-category-title,
    tutorial-embed-caption, skill-demo-caption, skill-source-card-subtitle,
    skill-references-heading, skill-reference-title
  main.css: hero-title-combined, hero-tagline-combined, impeccable-title,
    loading-state, install-primary-howto .install-path-desc em,
    install-howto-steps > li::before, install-step-status, consulting-title

These were dormant remnants of the retired Cormorant italic-serif look —
the kinpaku Pinstripe face renders them as bad synthesized-italic, but
no markup matches the selectors so nothing rendered. Removed only the
font-style declaration; the rest of each rule stays (whole-rule cleanup
is out of scope).

Kept the 5 live selectors (slop-section-heading, tutorial-card-title,
visual-mode-demo-caption, visual-mode-method-name, gallery-card-title)
per the "if they're not used anywhere" condition, plus .prose em (real
emphasis) and .prose blockquote (conventional blockquote italic).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: brand-seed palette.mjs + Setup step to run it

New-brand color now starts from a curated seed color (129 OKLCH seeds)
instead of the model guessing or defaulting to warm-cream. The script
returns one seed + composition guidance (pure-bg architecture, perceptual
text-on-fill, anti-cliché moods, jewel-tone range), with inverse-frequency
hue weighting for fair rainbow exposure and deterministic --from picking.
SKILL.md Setup step 5 makes it run for greenfield projects. Curation
tooling lives in the impeccable-evals repo (tools/palette/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Remove accidental live mode inject from Base.astro.

The localhost live.js tag was left in the site layout after a dev session and should never ship in the Astro template.

Co-authored-by: Cursor <cursoragent@cursor.com>

* site: dedicated /changelog + /faq, epic v3.5.0 notes, Live Mode → Beta

Split changelog and FAQ out of the homepage into two standalone kinpaku
pages, linked from the footer (and a quiet hint under the Get-started CTA).

/changelog: every release inline (no collapsible), newest first. The
v3.5.0 entry leads with a one-line summary, a real before/after pair from
the GPT-5.5 eval corpus (luxury-hotel brief, skill off vs on), and a stat
row (74% cream-bg, 76% extreme tracking, 90%+ low-contrast — measured
across ~190 samples). Then five scannable bold-led bullets, biggest
takeaway first: per-provider skill compilation, the bias-fix, Live Mode,
the 7 new detector rules, the tighter skill. Before/after JPGs optimized
to ~470KB total (down from ~2.5MB PNGs).

/faq: the six support questions, each deep-linkable.

Live Mode is now Beta everywhere it surfaces: the /live-mode eyebrow
badge and note, the homepage bento tile badge, and the changelog entry.
The historical v3.0 changelog entry stays "Alpha" — accurate to what
shipped then.

Footer trimmed to the four links not already in the top nav (Changelog,
FAQ, Privacy, GitHub).

Version bumped 3.2.0 → 3.5.0 across the three plugin manifests; the
3.2 bias-fix work folds into this release rather than shipping separately.

astro.config.mjs: disable the dev toolbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: point /design-system hero at the m-01-v2-01 finalist

design-system.css referenced kintsugi-hero-v2.png, an untracked orphan
that was never committed. Repoint it at the committed m-01-v2-01 finalist
so /design-system and the homepage hero share one image, and the page
no longer depends on a file outside the repo. The v2 orphan moved to tmp/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* build: sync harness mirrors + green the prose gate

Rebuild propagates the committed skill source (palette.mjs Setup step,
detector rule updates, brand.md) into the 13 harness output dirs and the
plugin subtree, which had drifted from source.

Also fixes the prose validator, which had been red on six pre-existing
hits across committed files:
- Four em dashes in code comments (Testimonials.astro, LiveDemoGbar.astro,
  index.astro) and one in skill/reference/live.md — reworded to colons/commas.
- Two in the slop catalog (an em-dash-overuse specimen and the
  marketing-buzzword rule naming "empower"). Those are intentional: the
  slop page documents every antipattern by example, so it must contain
  them. Exempted site/pages/slop from validateProse rather than neutering
  the specimens.

`bun run build` is now green end to end: counts validate, prose passes,
site builds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* skill: rewrite no-section-fade rule to fix Gemini zero-motion overcorrection

The old rule ("whole-section fade-on-scroll is the saturated AI motion
reflex") drove Gemini to overcorrect into shipping pages with no motion
at all: motion-variety 39% / zero-motion 12% with the skill on, vs
~74-78% variety and ~3% zero-motion without it.

Rewrite keeps the legitimate-stagger carve-out, names the defect at
shape level (one identical entrance on every section) without
enumerating motion primitives, and adds an explicit clause that
suppressing the reflex is never grounds for a static page.

Validated on Gemini 3.5-flash (n=10, luxury-hotel + infra-platform):
motion-variety 39% -> 70%, zero-motion 12% -> 0%, staggered-reveal
stays 0% (reflex not re-inflated).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* release: bump CLI to 2.2.0 and extension to 1.1.0

Both ship the expanded detector: the 7 new rules (cream-palette,
em-dash-overuse, marketing-buzzword, numbered-section-markers,
aphoristic-cadence, broken-image, italic-serif-display) plus
hero-eyebrow-chip, with everything-centered removed. 41 rules total.

The extension settings page already supports toggling them: the rule
list renders from detector/antipatterns.json, grouped by category, and
disabledRules flows through chrome.storage.sync into the scan config,
which detect.js honors by rule id. New rules are toggleable with no UI
change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* release: fix release.mjs for the moved changelog + add CLI/ext entries

The changelog moved from site/pages/index.astro to its own
site/pages/changelog.astro with new markup (cf-version / cf-entry /
cf-items), which left release.mjs reading the wrong file with the old
selectors. All three release commands would have failed at note
extraction. Point it at changelog.astro, match cf-version, and scope
notes to the <ul class="cf-items"> bullet list — that also skips the
lead paragraph, before/after figure, and stat row on the v3.5.0 entry,
keeping release notes to clean bullets.

Add CLI v2.2.0 and Extension v1.1.0 changelog entries (the shared
detector update: 7 new rules, everything-centered removed, 41 total;
plus the extension's per-rule toggles) so release:cli and release:ext
have notes to extract.

Verified extraction for all three labels: v3.5.0 (5 bullets),
CLI v2.2.0 (3), Extension v1.1.0 (2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: correct dev server port to 4321 and drop stale pnpm-lock

Astro serves on 4321, not 3000 as the docs claimed; update CLAUDE.md,
AGENTS.md, and screenshot-antipatterns.js. Remove the leftover
pnpm-lock.yaml from the Astro migration so Cloudflare's frozen install
uses the maintained, in-sync bun.lock instead of a drifted pnpm lockfile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: rework /designing flow, rhythm, Live Mode mock, and CTA

Restructure the page so iteration reads as the core value, not net-new.
The four loop phases are wrapped in a track with a sticky scroll-spy nav
(Start/Iterate/Polish/Maintain) that pins under the header and highlights
the active phase; the surfaces section (skill/CLI/extension) moves out of
the loop into the post-loop context group so the loop runs uninterrupted.

Fix the iterate split: shared subgrid row tracks so the terminal and the
Live Mode mock align on the same baseline regardless of paragraph length,
wider intro measure (52ch, was a crammed 36ch), and a deeper picker stage
so the context and global bars breathe instead of stacking on the card.

Rebuild the Live Mode mock to mirror the real picker: carved-tile mark plus
Pick / Insert / Detect / DESIGN.md controls on lacquer-deep with the gold
border, and a /impeccable live entry line so the reader knows how to start.

Reframe Start as the hard mode, move h3 subheads off the thin display face
onto Albert Sans, and trim Start so it no longer dominates the loop.

Rework the closing CTA into two standalone raised cards (the bento plinth
made them read as boxes nested in a box), and fix the tutorials copy: there
are three walkthroughs now, and the brand-vs-product tutorial is gone, so
drop it from the CTA and remove the dead lane link to it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: reorder Get Started so usage follows setup, link out to more

Move the /impeccable usage examples below the Chrome extension, CLI, and
Stay-updated block. Running a command is the logical next step once the
skill, extension, CLI, and subscriptions are all in place, so the section
now reads install -> set up the extras -> use it. Add a closing "Go deeper"
line linking to the Designing with Impeccable workflow page and the docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: install compiled per-provider skill variants, not uncompiled source

`npx skills add` (and `impeccable skills install`, which wrapped it) installed
the uncompiled skill/ source verbatim: the skills CLI dedupes discovery by name
and picks skill/SKILL.md first, so installs shipped unresolved {{placeholders}}
and no vendored detector (#168).

- Rename skill/SKILL.md -> skill/SKILL.src.md so the skills CLI's discovery
  skips the source and falls through to a compiled .agents variant; update the
  build reader, skill-behavior harness, and docs to match.
- Refactor `impeccable skills install` to copy each harness's compiled variant
  from the universal bundle (real dirs, no npx skills, no symlink), with
  project/global harness detection and a --providers override.
- Fix stale unit tests (replacePlaceholders, readPatterns, transformer
  prefix/summary) that asserted removed pre-v3.0 behavior, and wire the three
  orphaned test files into `bun run test` so the drift can't recur.
- Split skills-cli.test.js: pure blocks run by default, network blocks move
  behind a new `bun run test:cli-e2e`; fix its stale update assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: default to `npx impeccable skills install`, restore install-method panel

Get Started recommended `npx skills add`, which installs a single shared build
across harnesses. Make our CLI the default (it installs the build compiled for
each harness) and bring back the "Other install methods" disclosure the
neo-kinpaku redesign dropped.

- Homepage: primary command is now `npx impeccable skills install`; a native
  <details> panel offers the Claude Code plugin and `npx skills` (caveated as
  installing one shared build rather than the per-harness one).
- FAQ: recommend `npx impeccable skills install` to install, `--force` to
  reinstall, and note the npx skills shared-build caveat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* site: reword craft tagline so it doesn't lead with "Shape"

The craft card's tagline began with the word "Shape", which reads like
the name of the sibling /shape command and made the two cards look
swapped (#166). Reword to "Design it, then build it, all in one flow."
No data was actually swapped; this is a copy collision fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* skill: rename teach -> init and expand its setup flow

Rename the `/impeccable teach` command to `/impeccable init` across the
skill, site, CLI, and tests. `teach` stays as a deprecated router alias and
/docs/teach + /skills/teach redirect to /docs/init.

Expand the command beyond writing PRODUCT.md/DESIGN.md: the same codebase
crawl now also pre-configures `.impeccable/live/config.json` (Step 6, with
CSP consent) so live mode boots with no first-time detour, and the flow ends
by recommending the best commands to run next from what the scan surfaced
(Step 7).

Fold two items into the unreleased v3.5.0 changelog entry: the init rename
and the brand-seed palette picker. No version bump.

Regenerates all harness skill output dirs and the _redirects file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: lead README install + usage with the CLI installer

Add `npx impeccable skills install` as the recommended install option and
update the Usage section to the `/impeccable <command>` form, dropping the
nonexistent `/normalize` example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(skill-behavior): swap to production-tier models (sonnet + gpt-5.5)

Replace the cheap-tier default lineup (claude-haiku-4-5, gpt-5.4-mini) with
production-tier models (claude-sonnet-4-6, gpt-5.5) so the skill-behavior
suite reflects what users actually run. gemini stays on flash-lite.

Sync the docs (CLAUDE.md, AGENTS.md, tests/skill-behavior/README.md): new
model names, cost estimate raised to ~$0.50-1.50/sweep, and the old 21-22/24
baseline reframed as previous-cheap-tier history pending re-measurement on
the new lineup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: self-updating skill via boot-time version check

context.mjs now polls a new lightweight /api/version endpoint at most once
per day (cached globally in ~/.impeccable) and appends an UPDATE_AVAILABLE
directive when a newer skill version has shipped, prompting the agent to
offer `npx impeccable skills update`. Best-effort and silent on any failure;
asks before updating; suppresses re-prompts for a declined version for a
week. Opt out with IMPECCABLE_NO_UPDATE_CHECK=1.

- skill/scripts/context.mjs: version read, throttle + anti-nag cache, directive
- scripts/build.js + _redirects: /api/version endpoint (from plugin.json version)
- skill/SKILL.src.md: document the UPDATE_AVAILABLE boot branch
- tests/context.test.mjs: coverage for cached/newer/suppressed/opt-out paths
- changelog: v3.5.0 entry
- synced harness skill dirs via bun run build

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: cover the self-update path (network + LLM behavior)

context.test.mjs: add a localhost stub-server integration test for the live
fetch path (poll /api/version, cache a newer version, stay silent on
same-or-older, fail silent + stamp lastCheck when unreachable). Runs against
127.0.0.1 only, never the real site; uses async spawn so the in-process stub
isn't deadlocked by spawnSync blocking the event loop.

skill-behavior: add scenario 9 asserting the agent surfaces UPDATE_AVAILABLE
but never auto-runs `npx impeccable skills update` without asking. New
prepareWorkspace `skillVersion` copy-mode (so context.mjs has a SKILL.md to
version-check), env threading through runTurn -> execBash, and bash-output
capture to prove the agent actually received the directive. Passed on
claude-sonnet-4-6, gpt-5.5, and gemini-3.1-flash-lite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Paul Bakaus
2026-05-28 14:22:22 -07:00
committed by GitHub
co-authored by Claude Opus 4.7 Cursor
parent 84135db0e6
commit 9ffd3211d5
955 changed files with 121730 additions and 38852 deletions
+121
View File
@@ -188,3 +188,124 @@ Test thoroughly across contexts:
- **Slow connections**: Test on throttled network
When the adaptation feels native to each context, hand off to `{{command_prefix}}impeccable polish` for the final pass.
---
## Reference Material
The sections below were previously `responsive-design.md` and live inline now so the adapt flow has its deep responsive reference in one place.
### Responsive Design
#### Mobile-First: Write It Right
Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
#### Breakpoints: Content-Driven
Don't chase device sizes; let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
#### Detect Input Method, Not Just Screen Size
**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard. Use pointer and hover queries:
```css
/* Fine pointer (mouse, trackpad) */
@media (pointer: fine) {
.button { padding: 8px 16px; }
}
/* Coarse pointer (touch, stylus) */
@media (pointer: coarse) {
.button { padding: 12px 20px; } /* Larger touch target */
}
/* Device supports hover */
@media (hover: hover) {
.card:hover { transform: translateY(-2px); }
}
/* Device doesn't support hover (touch) */
@media (hover: none) {
.card { /* No hover state - use active instead */ }
}
```
**Critical**: Don't rely on hover for functionality. Touch users can't hover.
#### Safe Areas: Handle the Notch
Modern phones have notches, rounded corners, and home indicators. Use `env()`:
```css
body {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
/* With fallback */
.footer {
padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
```
**Enable viewport-fit** in your meta tag:
```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
```
#### Responsive Images: Get It Right
##### srcset with Width Descriptors
```html
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w
"
sizes="(max-width: 768px) 100vw, 50vw"
alt="Hero image"
>
```
**How it works**:
- `srcset` lists available images with their actual widths (`w` descriptors)
- `sizes` tells the browser how wide the image will display
- Browser picks the best file based on viewport width AND device pixel ratio
##### Picture Element for Art Direction
When you need different crops/compositions (not just resolutions):
```html
<picture>
<source media="(min-width: 768px)" srcset="wide.jpg">
<source media="(max-width: 767px)" srcset="tall.jpg">
<img src="fallback.jpg" alt="...">
</picture>
```
#### Layout Adaptation Patterns
**Navigation**: Three stages: hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
#### Testing: Don't Trust DevTools Alone
DevTools device emulation is useful for layout but misses:
- Actual touch interactions
- Real CPU/memory constraints
- Network latency patterns
- Font rendering differences
- Browser chrome/keyboard appearances
**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
---
**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.
+36 -10
View File
@@ -6,7 +6,7 @@ Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut moti
## Register
Brand: orchestrated page-load sequences, staggered reveals, scroll-driven animation. Motion is part of the voice; one well-rehearsed entrance beats scattered micro-interactions.
Brand: motion is part of the voice; one well-rehearsed entrance beats scattered micro-interactions. The saturated AI default is fade-and-rise reveals on every scrolled section; that's a tell, not a choreography. Reserve scroll-triggered motion for moments that earn it.
Product: 150250 ms on most transitions. Motion conveys state: feedback, reveal, loading, transitions between views. No page-load choreography; users are in a task and won't wait for it.
@@ -49,10 +49,11 @@ Create a purposeful animation plan:
Add motion systematically across these categories:
### Entrance Animations
- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
- **Content reveals**: Scroll-triggered animations using intersection observer
- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
- **List rhythm**: Sibling stagger is legitimate for cards-in-a-grid or list-items-appearing. Whole-section fade-on-scroll is not a list and is not legitimate. Cap total stagger time: 10 items at 50ms each = 500ms total. For more items, reduce per-item delay or cap the staggered count.
Use CSS custom properties for clean stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"`, `style="--i: 1"`, etc. on each item.
### Micro-interactions
- **Button feedback**:
@@ -97,11 +98,14 @@ Use appropriate techniques for each animation:
### Timing & Easing
**Durations by purpose:**
- **100-150ms**: Instant feedback (button press, toggle)
- **200-300ms**: State changes (hover, menu open)
- **300-500ms**: Layout changes (accordion, modal)
- **500-800ms**: Entrance animations (page load)
**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right":
| Duration | Use Case | Examples |
|----------|----------|----------|
| **100150ms** | Instant feedback | Button press, toggle, color change |
| **200300ms** | State changes | Menu open, tooltip, hover state |
| **300500ms** | Layout changes | Accordion, modal, drawer |
| **500800ms** | Entrance animations | Page load, hero reveal |
**Easing curves (use these, not CSS defaults):**
```css
@@ -134,13 +138,35 @@ Use appropriate techniques for each animation:
- GSAP for complex sequences
```
### Motion Materials
Transform and opacity are reliable defaults, not the whole palette. Premium interfaces often need atmospheric properties. Match material to effect:
- **Transform / opacity**: movement, press feedback, simple reveals, list choreography
- **Blur / filter / backdrop-filter**: focus pulls, depth, glass or lens effects, softened entrances
- **Clip-path / masks**: wipes, reveals, editorial cropping, product-like transitions
- **Shadow / glow / color filters**: energy, affordance, focus, warmth, active state
- **Grid-template-rows or FLIP-style transforms**: expanding and reflowing layout without animating `height` directly
The hard rule isn't "transform and opacity only." It's: avoid animating layout-driving properties casually (`width`, `height`, `top`, `left`, margins), keep expensive effects bounded to small or isolated areas, and verify smoothness in-browser on target viewports.
### Performance
- **Motion materials**: Use transform/opacity for reliable movement, but use blur, filters, masks, shadows, and color shifts when they materially improve the effect
- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins)
- **will-change**: Add sparingly for known expensive animations
- **will-change**: Add sparingly for known expensive animations only (e.g. on `:hover` or an `.animating` class), never preemptively across the whole page
- **Scroll triggers**: Use Intersection Observer instead of scroll event listeners; unobserve after the animation fires once
- **Bound expensive effects**: Keep blur/filter/shadow areas small or isolated, use `contain` where appropriate
- **Monitor FPS**: Ensure 60fps on target devices
### Perceived Performance
Nobody cares how fast your site *is*, only how fast it feels. The 80ms threshold: anything under ~80ms feels instant because our brains buffer sensory input for that long to synchronize perception. Target this for micro-interactions.
- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
- **Early completion**: Show content progressively, don't wait for everything (progressive images, streaming HTML, skeleton fade-ins).
- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Use for low-stakes actions (likes, follows). Avoid for payments or destructive operations.
- **Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances.
- **Caution**: Too-fast responses can decrease perceived value for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
### Accessibility
```css
@media (prefers-reduced-motion: reduce) {
+5 -5
View File
@@ -50,7 +50,7 @@ Create a strategy to increase impact while maintaining coherence:
Systematically increase impact across these dimensions:
### Typography Amplification
- **Replace generic fonts**: Swap system fonts for distinctive choices (see the parent skill's typography guidelines and [typography.md](typography.md) for inspiration)
- **Replace generic fonts**: Swap system fonts for distinctive choices (see the parent skill's typography guidelines and the [Reference Material section of typeset.md](typeset.md#reference-material) for inspiration)
- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
@@ -78,10 +78,10 @@ Systematically increase impact across these dimensions:
- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
### Motion & Animation
- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic, which cheapen the effect)
- **Hero moment**: One signature entrance, once. Not on every visit and not on every section.
- **Micro-interactions**: Satisfying hover effects, click feedback, state changes.
- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic, which cheapen the effect).
- **Bolder ≠ scroll-fade-rise on every section.** That's the saturated AI default, the opposite of bold.
### Composition Boldness
- **Hero moments**: Create clear focal points with dramatic treatment
+34 -44
View File
@@ -18,7 +18,7 @@ Then the inverse test: in one sentence, describe what you're about to build the
### Font selection procedure
Every project. Never skip.
Every project. Never skip. <!-- rule:brand-typo-font-selection-procedure -->
1. Read the brief. Write three concrete brand-voice words. Not "modern" or "elegant," but "warm and mechanical and opinionated" or "calm and clinical and careful." Physical-object words.
2. List the three fonts you'd reach for by reflex. If any appear in the reflex-reject list below, reject them; they are training-data defaults and they create monoculture.
@@ -27,13 +27,13 @@ Every project. Never skip.
### Reflex-reject list
Training-data defaults. Ban list. Look further:
Training-data defaults. Ban list. Look further: <!-- rule:brand-typo-reflex-reject-fonts -->
Fraunces · Newsreader · Lora · Crimson · Crimson Pro · Crimson Text · Playfair Display · Cormorant · Cormorant Garamond · Syne · IBM Plex Mono · IBM Plex Sans · IBM Plex Serif · Space Mono · Space Grotesk · Inter · DM Sans · DM Serif Display · DM Serif Text · Outfit · Plus Jakarta Sans · Instrument Sans · Instrument Serif
### Reflex-reject aesthetic lanes
Parallel to the font list. Currently saturated aesthetic families that have flooded brand surfaces. If a brief lands in one of these lanes without a register reason that *requires* it (a literal magazine, a literal terminal, a literal industrial signage system), it's the second-order training reflex: the trap one tier deeper than picking a Fraunces font. Look further.
Parallel to the font list. Currently saturated aesthetic families that have flooded brand surfaces. If a brief lands in one of these lanes without a register reason that *requires* it (a literal magazine, a literal terminal, a literal industrial signage system), it's the second-order training reflex: the trap one tier deeper than picking a Fraunces font. Look further. <!-- rule:brand-typo-reflex-reject-lanes -->
- **Editorial-typographic.** Display serif (often italic) + small mono labels + ruled separators + monochromatic restraint. Klim-influenced, magazine-cover affectation. By 2026, every Stripe-adjacent and Notion-adjacent brand has landed here. The fingerprint: three rule-separated columns, an italic Fraunces / Recoleta / Newsreader headline, lowercase track-spaced metadata, no imagery.
@@ -43,76 +43,66 @@ The reflex-reject lists apply to **new design choices**. When the existing brand
### Pairing and voice
Distinctive + refined is the goal. The specific shape depends on the brand:
- **Editorial / long-form / luxury**: display serif + sans body (a magazine shape).
- **Tech / dev tools / fintech**: one committed sans, usually; custom-tight tracking, strong weight contrast inside a single family.
- **Consumer / food / travel**: warmer pairings, often a humanist sans plus a script or display serif.
- **Creative studios / agencies**: rule-breaking welcome. Mono-only, or display-only, or custom-drawn type as voice.
Distinctive + refined is the goal. The specific shape depends on the brand, not on the brand's category. A category ("restaurant", "dev tool", "magazine", "fintech") is not a recipe; treating it as one is the first-order reflex SKILL.md warns against. <!-- rule:brand-typo-pairing-voice -->
Two families minimum is the rule *only* when the voice needs it. A single well-chosen family with committed weight/size contrast is stronger than a timid display+body pair.
Vary across projects. If the last brief was a serif-display landing page, this one isn't.
### Scale
Modular scale, fluid `clamp()` for headings, ≥1.25 ratio between steps. Flat scales (1.1× apart) read as uncommitted.
Modular scale, fluid `clamp()` for headings, ≥1.25 ratio between steps. Flat scales (1.1× apart) read as uncommitted. <!-- rule:brand-typo-modular-scale -->
Light text on dark backgrounds: add 0.050.1 to line-height. Light type reads as lighter weight and needs more breathing room.
Light text on dark backgrounds: add 0.050.1 to line-height. Light type reads as lighter weight and needs more breathing room. <!-- rule:brand-typo-light-on-dark-leading -->
## Color
Brand surfaces have permission for Committed, Full palette, and Drenched strategies. Use them. A single saturated color spread across a hero is not excess; it's voice. A beige-and-muted-slate landing page ignores the register.
Brand surfaces have permission for Committed, Full palette, and Drenched strategies. Use them. A single saturated color spread across a hero is not excess; it's voice. A beige-and-muted-slate landing page ignores the register. <!-- rule:brand-color-strategy-permission -->
- Name a real reference before picking a strategy. "Klim Type Foundry #ff4500 orange drench", "Stripe purple-on-white restraint", "Liquid Death acid-green full palette", "Mailchimp yellow full palette", "Condé Nast Traveler muted navy restraint", "Vercel pure black monochrome". Unnamed ambition becomes beige.
- Palette IS voice. A calm brand and a restless brand should not share palette mechanics.
- When the strategy is Committed or Drenched, color carries the brand. Don't hedge with neutrals around the edges. Commit.
- Don't converge across projects. If the last brand surface was restrained-on-cream, this one is not.
- When a cultural-symbol palette is the obvious pull, reach past it. Let the cultural reading come from typography, imagery, and copy, not the palette.
- Name a real reference before picking a strategy. "Klim Type Foundry #ff4500 orange drench", "Stripe purple-on-white restraint", "Liquid Death acid-green full palette", "Mailchimp yellow full palette", "Condé Nast Traveler muted navy restraint", "Vercel pure black monochrome". Unnamed ambition becomes beige. <!-- rule:brand-color-named-reference -->
- Palette IS voice. A calm brand and a restless brand should not share palette mechanics. <!-- rule:brand-color-palette-is-voice -->
- When the strategy is Committed or Drenched, color carries the brand. Don't hedge with neutrals around the edges. Commit. <!-- rule:brand-color-commit-no-hedge -->
- Don't converge across projects. If the last brand surface was restrained-on-cream, this one is not. <!-- rule:brand-color-no-converge -->
- When a cultural-symbol palette is the obvious pull, reach past it. Let the cultural reading come from typography, imagery, and copy, not the palette. <!-- rule:brand-color-no-cultural-symbol -->
## Layout
- Asymmetric compositions are one option. Break the grid intentionally for emphasis.
- Fluid spacing with `clamp()` that breathes on larger viewports. Vary for rhythm: generous separations, tight groupings.
- Alternative: a strict, visible grid as the voice (brutalist / Swiss / tech-spec aesthetics). Either asymmetric or rigorously-gridded can be "designed"; the failure mode is splitting the difference into a generic centered stack.
- Don't default to centering everything. Left-aligned with asymmetric layouts feels more designed; a strict grid reads as confident structure. A centered-stack hero with icon-title-subtitle cards reads as template.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` for breakpoint-free responsiveness.
- Asymmetric compositions are one option. Break the grid intentionally for emphasis. <!-- rule:brand-layout-asymmetric -->
- Fluid spacing with `clamp()` that breathes on larger viewports. Vary for rhythm: generous separations, tight groupings. <!-- rule:brand-layout-fluid-spacing -->
- For image-led briefs (hotels, restaurants, magazines, photography), full-bleed hero imagery with overlaid menu and centered headline is a canonical move; let the photograph be the design. <!-- rule:brand-layout-image-led-hero -->
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` for breakpoint-free responsiveness. <!-- rule:brand-layout-cards-when-right -->
## Imagery
Brand surfaces lean on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
**When the brief implies imagery (restaurants, hotels, magazines, photography, hobbyist communities, food, travel, fashion, product), you must ship imagery.** Zero images is a bug, not a design choice. "Restraint" is not an excuse. If the approved comp or brief is image-led, ship real project assets, generated raster assets, or a credible canvas/SVG/WebGL scene. Do not replace photographic, architectural, product, or place imagery with generic CSS panels, decorative diagrams, cards, bullets, or copy.
**When the brief implies imagery (restaurants, hotels, magazines, photography, hobbyist communities, food, travel, fashion, product), you must ship imagery.** Zero images is a bug, not a design choice. "Restraint" is not an excuse. If the approved comp or brief is image-led, ship real project assets, generated raster assets, or a credible canvas/SVG/WebGL scene. Do not replace photographic, architectural, product, or place imagery with generic CSS panels, decorative diagrams, cards, bullets, or copy. <!-- rule:brand-imagery-required -->
- **For greenfield work without local assets, use stock imagery.** Unsplash is the default. The URL shape is `https://images.unsplash.com/photo-{id}?auto=format&fit=crop&w=1600&q=80`. **Verify the URLs before referencing them.** If you have an image-search MCP, web-fetch tool, or browser access, use it to find real photo IDs and confirm they resolve. Guessed IDs (even ones that look real) often 404 and ship as broken-image placeholders. Without a verification path, pick fewer photos you're confident exist over more that you guessed; never substitute colored `<div>` placeholders.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
- **For greenfield work without local assets, use stock imagery.** Unsplash is the default. The URL shape is `https://images.unsplash.com/photo-{id}?auto=format&fit=crop&w=1600&q=80`. **Verify the URLs before referencing them.** If you have an image-search MCP, web-fetch tool, or browser access, use it to find real photo IDs and confirm they resolve. Guessed IDs (even ones that look real) often 404 and ship as broken-image placeholders. Without a verification path, pick fewer photos you're confident exist over more that you guessed; never substitute colored `<div>` placeholders. <!-- rule:brand-imagery-unsplash-default -->
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel". <!-- rule:brand-imagery-physical-object-search -->
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one. <!-- rule:brand-imagery-one-decisive-photo -->
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish". <!-- rule:brand-imagery-alt-text -->
"Imagery" here is broader than stock photography: product screenshots, custom data visualizations, generated SVG, and canvas/WebGL scenes are all imagery. Text-only pages where typography alone carries the entire visual weight are the failure mode.
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions, when the brand invites it. Tech-minimal brands often skip entrance motion entirely; the restraint is the voice.
- For collapsing/expanding sections, transition `grid-template-rows` rather than `height`.
- One well-orchestrated page-load beats scattered micro-interactions, when the brand invites it. Some brands skip entrance motion entirely; the restraint is the voice. <!-- rule:brand-motion-one-page-load -->
## Brand bans (on top of the shared absolute bans)
- Monospace as lazy shorthand for "technical / developer." If the brand isn't technical, mono reads as costume.
- Large rounded-corner icons above every heading. Screams template.
- Single-family pages that picked the family by reflex, not voice. (A single family chosen deliberately is fine.)
- All-caps body copy. Reserve caps for short labels and headings.
- Timid palettes and average layouts. Safe = invisible.
- Zero imagery on a brief that implies imagery (restaurant, hotel, food, travel, fashion, photography, hobbyist). Colored blocks where a hero photo belongs.
- Defaulting to editorial-magazine aesthetics (display serif + italic + drop caps + broadsheet grid) on briefs that aren't magazine-shaped. Editorial is ONE aesthetic lane, not the default brand aesthetic.
- Repeated tiny uppercase tracked labels above every section heading. A single strong kicker can be voice; repeating it as section grammar is AI scaffolding unless it's a deliberate, named brand system.
- Monospace as lazy shorthand for "technical / developer." If the brand isn't technical, mono reads as costume. <!-- rule:brand-ban-mono-as-shorthand -->
- Large rounded-corner icons above every heading. Screams template. <!-- rule:brand-ban-large-rounded-icons -->
- Single-family pages that picked the family by reflex, not voice. (A single family chosen deliberately is fine.) <!-- rule:brand-ban-single-family-by-reflex -->
- All-caps body copy. Reserve caps for short labels and headings. <!-- rule:brand-ban-all-caps-body -->
- Timid palettes and average layouts. Safe = invisible. <!-- rule:brand-ban-timid-palettes -->
- Zero imagery on a brief that implies imagery (restaurant, hotel, food, travel, fashion, photography, hobbyist). Colored blocks where a hero photo belongs. <!-- rule:brand-ban-zero-imagery -->
- Defaulting to editorial-magazine aesthetics (display serif + italic + drop caps + broadsheet grid) on briefs that aren't magazine-shaped. Editorial is ONE aesthetic lane, not the default brand aesthetic. <!-- rule:brand-ban-editorial-default -->
- Repeated tiny uppercase tracked labels above every section heading. A single strong kicker can be voice; repeating it as section grammar is AI scaffolding unless it's a deliberate, named brand system. <!-- rule:brand-ban-repeated-section-kickers -->
## Brand permissions
Brand can afford things product can't. Take them.
- Ambitious first-load motion. Reveals, scroll-triggered transitions, typographic choreography.
- Single-purpose viewports. One dominant idea per fold, long scroll, deliberate pacing.
- Typographic risk. Enormous display type, unexpected italic cuts, mixed cases, hand-drawn headlines, a single oversize word as a hero.
- Unexpected color strategies. Palette IS voice; a calm brand and a restless brand should not share palette mechanics.
- Art direction per section. Different sections can have different visual worlds if the narrative demands it. Consistency of voice beats consistency of treatment.
- Ambitious first-load motion. Reveals and typographic choreography that earn their place; not fade-on-scroll for every section. <!-- rule:brand-permission-first-load-motion -->
- Single-purpose viewports. One dominant idea per fold, long scroll, deliberate pacing. <!-- rule:brand-permission-single-purpose-viewports -->
- Unexpected color strategies. Palette IS voice; a calm brand and a restless brand should not share palette mechanics. <!-- rule:brand-permission-unexpected-color -->
- Art direction per section. Different sections can have different visual worlds if the narrative demands it. Consistency of voice beats consistency of treatment. <!-- rule:brand-permission-art-direction -->
+114
View File
@@ -172,3 +172,117 @@ Test that copy improvements work:
- **Tone**: Is it appropriate for the situation?
When the copy reads cleanly, hand off to `{{command_prefix}}impeccable polish` for the final pass.
---
## Reference Material
The sections below were previously `ux-writing.md` and live inline now so the clarify flow has its deep UX-writing reference in one place.
### UX Writing
#### The Button Label Problem
**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
| Bad | Good | Why |
|-----|------|-----|
| OK | Save changes | Says what will happen |
| Submit | Create account | Outcome-focused |
| Yes | Delete message | Confirms the action |
| Cancel | Keep editing | Clarifies what "cancel" means |
| Click here | Download PDF | Describes the destination |
**For destructive actions**, name the destruction:
- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
- "Delete 5 items" not "Delete selected" (show the count)
#### Error Messages: The Formula
Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
##### Error Message Templates
| Situation | Template |
|-----------|----------|
| **Format error** | "[Field] needs to be [format]. Example: [example]" |
| **Missing required** | "Please enter [what's missing]" |
| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
##### Don't Blame the User
Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
#### Empty States Are Opportunities
Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
#### Voice vs Tone
**Voice** is your brand's personality, consistent everywhere.
**Tone** adapts to the moment.
| Moment | Tone Shift |
|--------|------------|
| Success | Celebratory, brief: "Done! Your changes are live." |
| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
| Loading | Reassuring: "Saving your work..." |
| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
#### Writing for Accessibility
**Link text** must have standalone meaning: "View pricing plans" not "Click here". **Alt text** describes information, not the image: "Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
#### Writing for Translation
##### Plan for Expansion
German text is ~30% longer than English. Allocate space:
| Language | Expansion |
|----------|-----------|
| German | +30% |
| French | +20% |
| Finnish | +30-40% |
| Chinese | -30% (fewer chars, but same width) |
##### Translation-Friendly Patterns
Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
#### Consistency: The Terminology Problem
Pick one term and stick with it:
| Inconsistent | Consistent |
|--------------|------------|
| Delete / Remove / Trash | Delete |
| Settings / Preferences / Options | Settings |
| Sign in / Log in / Enter | Sign in |
| Create / Add / New | Create |
Build a terminology glossary and enforce it. Variety creates confusion.
#### Avoid Redundant Copy
If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
#### Loading States
Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
#### Confirmation Dialogs: Use Sparingly
Most confirmation dialogs are design failures; consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
#### Form Instructions
Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
---
**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.
+2 -2
View File
@@ -23,8 +23,8 @@ Before generating anything, run a brief direction conversation grounded in the s
Ask **2-3 targeted questions** about visual lane, color strategy, atmosphere, and named anchor references. Don't enumerate generic menus; tie each question to the shape brief's answers. Example shape-grounded questions:
- "Brief says 'editorial restraint, Klim-adjacent.' Are we closer to a quiet specimen page or a magazine-spread feel with hero imagery?"
- "Palette strategy from shape was 'Committed.' Want it warm-grounded (deep oxblood + cream) or cool-grounded (slate + paper white)?"
- "Brief says 'specimen-page restraint.' Are we closer to a quiet typographic page or a wider editorial spread with hero imagery?"
- "Palette strategy from shape was 'Committed.' Which one color carries the surface (a brand-driven pick rather than a default warm-or-cool framing)? (And no, the answer isn't a cream/sand body bg; that's the saturated AI default.)"
**STOP and wait for answers.** These pin the palette before any pixel gets generated. Do not proceed to Step B until the user has responded.
-106
View File
@@ -1,106 +0,0 @@
# Cognitive Load Assessment
Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
---
## Three Types of Cognitive Load
### Intrinsic Load: The Task Itself
Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
**Manage it by**:
- Breaking complex tasks into discrete steps
- Providing scaffolding (templates, defaults, examples)
- Progressive disclosure: show what's needed now, hide the rest
- Grouping related decisions together
### Extraneous Load: Bad Design
Mental effort caused by poor design choices. **Eliminate this ruthlessly.** It's pure waste.
**Common sources**:
- Confusing navigation that requires mental mapping
- Unclear labels that force users to guess meaning
- Visual clutter competing for attention
- Inconsistent patterns that prevent learning
- Unnecessary steps between user intent and result
### Germane Load: Learning Effort
Mental effort spent building understanding. This is *good* cognitive load; it leads to mastery.
**Support it by**:
- Progressive disclosure that reveals complexity gradually
- Consistent patterns that reward learning
- Feedback that confirms correct understanding
- Onboarding that teaches through action, not walls of text
---
## Cognitive Load Checklist
Evaluate the interface against these 8 items:
- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
**Scoring**: Count the failed items. 01 failures = low cognitive load (good). 23 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
---
## The Working Memory Rule
**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
- **≤4 items**: Within working memory limits, manageable
- **57 items**: Pushing the boundary; consider grouping or progressive disclosure
- **8+ items**: Overloaded; users will skip, misclick, or abandon
**Practical applications**:
- Navigation menus: ≤5 top-level items (group the rest under clear categories)
- Form sections: ≤4 fields visible per group before a visual break
- Action buttons: 1 primary, 12 secondary, group the rest in a menu
- Dashboard widgets: ≤4 key metrics visible without scrolling
- Pricing tiers: ≤3 options (more causes analysis paralysis)
---
## Common Cognitive Load Violations
### 1. The Wall of Options
**Problem**: Presenting 10+ choices at once with no hierarchy.
**Fix**: Group into categories, highlight recommended, use progressive disclosure.
### 2. The Memory Bridge
**Problem**: User must remember info from step 1 to complete step 3.
**Fix**: Keep relevant context visible, or repeat it where it's needed.
### 3. The Hidden Navigation
**Problem**: User must build a mental map of where things are.
**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
### 4. The Jargon Barrier
**Problem**: Technical or domain language forces translation effort.
**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
### 5. The Visual Noise Floor
**Problem**: Every element has the same visual weight; nothing stands out.
**Fix**: Establish clear hierarchy: one primary element, 23 secondary, everything else muted.
### 6. The Inconsistent Pattern
**Problem**: Similar actions work differently in different places.
**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
### 7. The Multi-Task Demand
**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
**Fix**: Sequence the steps. Let the user do one thing at a time.
### 8. The Context Switch
**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.
-105
View File
@@ -1,105 +0,0 @@
# Color & Contrast
## Color Spaces: Use OKLCH
**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal, unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness, but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex; those are the dominant AI-design defaults, not the right answer for any specific brand.
## Building Functional Palettes
### Tinted Neutrals
**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
### Palette Structure
A complete system needs:
| Role | Purpose | Example |
|------|---------|---------|
| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
### The 60-30-10 Rule (Applied Correctly)
This rule is about **visual weight**, not pixel count:
- **60%**: Neutral backgrounds, white space, base surfaces
- **30%**: Secondary colors: text, borders, inactive states
- **10%**: Accent: CTAs, highlights, focus states
The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
## Contrast & Accessibility
### WCAG Requirements
| Content Type | AA Minimum | AAA Target |
|--------------|------------|------------|
| Body text | 4.5:1 | 7:1 |
| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
| UI components, icons | 3:1 | 4.5:1 |
| Non-essential decorations | None | None |
**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
### Dangerous Color Combinations
These commonly fail contrast or cause readability issues:
- Light gray text on white (the #1 accessibility fail)
- **Gray text on any colored background**: gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
- Red text on green background (or vice versa): 8% of men can't distinguish these
- Blue text on red background (vibrates visually)
- Yellow text on white (almost always fails)
- Thin light text on images (unpredictable contrast)
### Never Use Pure Gray or Pure Black
Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature; real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
### Testing
Don't trust your eyes. Use tools:
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- Browser DevTools → Rendering → Emulate vision deficiencies
- [Polypane](https://polypane.app/) for real-time testing
## Theming: Light & Dark Mode
### Dark Mode Is Not Inverted Light Mode
You can't just swap colors. Dark mode requires different design decisions:
| Light Mode | Dark Mode |
|------------|-----------|
| Shadows for depth | Lighter surfaces for depth (no shadows) |
| Dark text on light | Light text on dark (reduce font weight) |
| Vibrant accents | Desaturate accents slightly |
| White backgrounds | Never pure black; use dark gray (oklch 12-18%) |
In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project; do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
### Token Hierarchy
Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer; primitives stay the same.
## Alpha Is A Design Smell
Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
---
**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).
+107 -4
View File
@@ -68,10 +68,10 @@ Add color systematically across these dimensions:
- **Hover states**: Introduce color on interaction
### Background & Surfaces
- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
- **Tinted backgrounds**: If you replace pure gray, tint toward the brand hue, not toward a generic-warm-or-cool pair. The default-warm-tint (`oklch(97% 0.01 60)` and its neighbors) is now the AI cream/sand giveaway. Be specific to the brand or stay neutral.
- **Colored sections**: Use subtle background colors to separate areas
- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
- **Cards & surfaces**: Tint cards or surfaces toward the brand, not "for warmth" by reflex
**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
@@ -124,8 +124,6 @@ Ensure color addition improves rather than overwhelms:
- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
- Apply color randomly without semantic meaning
- Put gray text on colored backgrounds. It looks washed out; use a darker shade of the background color or transparency instead
- Use pure gray for neutrals. Add subtle color tint (warm or cool) for depth
- Use pure black (`#000`) or pure white (`#fff`) for large areas
- Violate WCAG contrast requirements
- Use color as the only indicator (accessibility issue)
- Make everything colorful (defeats the purpose)
@@ -152,3 +150,108 @@ When invoked from live mode, each variant MUST declare a `color-amount` param so
```
Layer 1-2 variant-specific params on top: palette selection (`steps` with named options), temperature warmth, or tint vs. true color. See `reference/live.md` for the full params contract.
---
## Reference Material
The sections below were previously `color-and-contrast.md` and live inline now so the colorize flow has its deep color reference in one place.
### Color & Contrast
#### Color Spaces: Use OKLCH
**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal, unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness, but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex; those are the dominant AI-design defaults, not the right answer for any specific brand.
#### Building Functional Palettes
##### Tinted Neutrals
**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
##### Palette Structure
A complete system needs:
| Role | Purpose | Example |
|------|---------|---------|
| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
##### The 60-30-10 Rule (Applied Correctly)
This rule is about **visual weight**, not pixel count:
- **60%**: Neutral backgrounds, white space, base surfaces
- **30%**: Secondary colors: text, borders, inactive states
- **10%**: Accent: CTAs, highlights, focus states
The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
#### Contrast & Accessibility
##### WCAG Requirements
| Content Type | AA Minimum | AAA Target |
|--------------|------------|------------|
| Body text | 4.5:1 | 7:1 |
| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
| UI components, icons | 3:1 | 4.5:1 |
| Non-essential decorations | None | None |
##### Dangerous Color Combinations
These commonly fail contrast or cause readability issues:
- Light gray text on white (the #1 accessibility fail)
- Red text on green background (or vice versa): 8% of men can't distinguish these
- Blue text on red background (vibrates visually)
- Yellow text on white (almost always fails)
- Thin light text on images (unpredictable contrast)
##### Testing
Don't trust your eyes. Use tools:
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- Browser DevTools → Rendering → Emulate vision deficiencies
- [Polypane](https://polypane.app/) for real-time testing
#### Theming: Light & Dark Mode
##### Dark Mode Is Not Inverted Light Mode
You can't just swap colors. Dark mode requires different design decisions:
| Light Mode | Dark Mode |
|------------|-----------|
| Shadows for depth | Lighter surfaces for depth (no shadows) |
| Dark text on light | Light text on dark (reduce font weight) |
| Vibrant accents | Desaturate accents slightly |
| White backgrounds | Either pure black or a deep surface that fits the brand (a brand-tinted near-black at oklch 12-18% works too) |
In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project; do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
##### Token Hierarchy
Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer; primitives stay the same.
#### Alpha Is A Design Smell
Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
---
**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Skipping color blindness testing (8% of men affected).
+6 -6
View File
@@ -56,15 +56,15 @@ If the harness has native image generation (Codex), a compact shape's "confirm o
Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
- [spatial-design.md](spatial-design.md) for layout and spacing
- [typography.md](typography.md) for type hierarchy
- [layout.md](layout.md) for layout, spacing, grid, container queries, optical adjustments
- [typeset.md](typeset.md) for type hierarchy, font selection, web font loading, OpenType features (Reference Material section)
Then add references based on the brief's needs:
- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
- Animation or transitions? Consult [motion-design.md](motion-design.md)
- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
- Animation or transitions? Consult [animate.md](animate.md) (Reference Material covers motion materials, durations, easing, perceived performance)
- Color-heavy or themed? Consult [colorize.md](colorize.md) (Reference Material covers OKLCH, palette structure, dark mode, contrast)
- Responsive requirements? Consult [adapt.md](adapt.md) (Reference Material covers breakpoints, input methods, safe areas, responsive images)
- Heavy on copy, labels, or errors? Consult [clarify.md](clarify.md) (Reference Material covers button labels, error formula, voice/tone, translation)
## Step 3: Visual Direction & Assets (Harness-Gated)
+538 -6
View File
@@ -48,9 +48,9 @@ Read relevant source files and visually inspect the live page when browser autom
Evaluate:
- **AI slop**: Would someone believe "AI made this" immediately? Check all DON'T guidance from the parent Impeccable skill.
- **Holistic design**: hierarchy, IA, emotional fit, discoverability, composition, typography, color, accessibility, states, copy, and edge cases.
- **Cognitive load**: consult [cognitive-load](cognitive-load.md); report checklist failures and decision points with >4 visible options.
- **Cognitive load**: consult the [Cognitive Load Assessment](#cognitive-load-assessment) section below; report checklist failures and decision points with >4 visible options.
- **Emotional journey**: peak-end rule, emotional valleys, reassurance at high-stakes moments.
- **Nielsen heuristics**: consult [heuristics-scoring](heuristics-scoring.md); score all 10 heuristics 0-4.
- **Nielsen heuristics**: consult the [Heuristics Scoring Guide](#heuristics-scoring-guide) section below; score all 10 heuristics 0-4.
Return: AI slop verdict, heuristic scores, cognitive load, emotional journey, 2-3 strengths, 3-5 priority issues, persona red flags, minor observations, and provocative questions.
@@ -102,7 +102,7 @@ Codex final-answer note: `$impeccable critique` produces a report artifact, so t
Structure your feedback as a design director would:
#### Design Health Score
> *Consult [heuristics-scoring](heuristics-scoring.md)*
> *Consult the [Heuristics Scoring Guide](#heuristics-scoring-guide) section below.*
Present the Nielsen's 10 heuristics scores as a table:
@@ -141,16 +141,16 @@ Highlight 2-3 things done well. Be specific about why they work.
#### Priority Issues
The 3-5 most impactful design problems, ordered by importance.
For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](heuristics-scoring.md) for severity definitions):
For each issue, tag with **P0-P3 severity** (see [Issue Severity below](#issue-severity-p0p3) for definitions):
- **[P?] What**: Name the problem clearly
- **Why it matters**: How this hurts users or undermines goals
- **Fix**: What to do about it (be concrete)
- **Suggested command**: Which command could address this (from: {{available_commands}})
#### Persona Red Flags
> *Consult [personas](personas.md)*
> *Consult the [Personas reference](#persona-based-design-testing) below.*
Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `{{config_file}}` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `{{config_file}}` contains a `## Design Context` section from `impeccable init`, also generate 1-2 project-specific personas from the audience/brand info.
For each selected persona, walk through the primary user action and list specific red flags found:
@@ -271,3 +271,535 @@ After presenting the summary, tell the user:
> You can ask me to run these one at a time, all at once, or in any order you prefer.
>
> Re-run `{{command_prefix}}impeccable critique` after fixes to see your score improve.
---
## Reference Material
The sections below were previously separate reference files (`cognitive-load.md`, `heuristics-scoring.md`, `personas.md`). They live inline now so the critique flow has all its deep context in one place.
### Cognitive Load Assessment
Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
---
#### Three Types of Cognitive Load
##### Intrinsic Load: The Task Itself
Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
**Manage it by**:
- Breaking complex tasks into discrete steps
- Providing scaffolding (templates, defaults, examples)
- Progressive disclosure: show what's needed now, hide the rest
- Grouping related decisions together
##### Extraneous Load: Bad Design
Mental effort caused by poor design choices. **Eliminate this ruthlessly.** It's pure waste.
**Common sources**:
- Confusing navigation that requires mental mapping
- Unclear labels that force users to guess meaning
- Visual clutter competing for attention
- Inconsistent patterns that prevent learning
- Unnecessary steps between user intent and result
##### Germane Load: Learning Effort
Mental effort spent building understanding. This is *good* cognitive load; it leads to mastery.
**Support it by**:
- Progressive disclosure that reveals complexity gradually
- Consistent patterns that reward learning
- Feedback that confirms correct understanding
- Onboarding that teaches through action, not walls of text
---
#### Cognitive Load Checklist
Evaluate the interface against these 8 items:
- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
**Scoring**: Count the failed items. 01 failures = low cognitive load (good). 23 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
---
#### The Working Memory Rule
**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
- **≤4 items**: Within working memory limits, manageable
- **57 items**: Pushing the boundary; consider grouping or progressive disclosure
- **8+ items**: Overloaded; users will skip, misclick, or abandon
**Practical applications**:
- Navigation menus: ≤5 top-level items (group the rest under clear categories)
- Form sections: ≤4 fields visible per group before a visual break
- Action buttons: 1 primary, 12 secondary, group the rest in a menu
- Dashboard widgets: ≤4 key metrics visible without scrolling
- Pricing tiers: ≤3 options (more causes analysis paralysis)
---
#### Common Cognitive Load Violations
##### 1. The Wall of Options
**Problem**: Presenting 10+ choices at once with no hierarchy.
**Fix**: Group into categories, highlight recommended, use progressive disclosure.
##### 2. The Memory Bridge
**Problem**: User must remember info from step 1 to complete step 3.
**Fix**: Keep relevant context visible, or repeat it where it's needed.
##### 3. The Hidden Navigation
**Problem**: User must build a mental map of where things are.
**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
##### 4. The Jargon Barrier
**Problem**: Technical or domain language forces translation effort.
**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
##### 5. The Visual Noise Floor
**Problem**: Every element has the same visual weight; nothing stands out.
**Fix**: Establish clear hierarchy: one primary element, 23 secondary, everything else muted.
##### 6. The Inconsistent Pattern
**Problem**: Similar actions work differently in different places.
**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
##### 7. The Multi-Task Demand
**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
**Fix**: Sequence the steps. Let the user do one thing at a time.
##### 8. The Context Switch
**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.
---
### Heuristics Scoring Guide
Score each of Nielsen's 10 Usability Heuristics on a 04 scale. Be honest: a 4 means genuinely excellent, not "good enough."
#### Nielsen's 10 Heuristics
##### 1. Visibility of System Status
Keep users informed about what's happening through timely, appropriate feedback.
**Check for**:
- Loading indicators during async operations
- Confirmation of user actions (save, submit, delete)
- Progress indicators for multi-step processes
- Current location in navigation (breadcrumbs, active states)
- Form validation feedback (inline, not just on submit)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | No feedback; user is guessing what happened |
| 1 | Rare feedback; most actions produce no visible response |
| 2 | Partial; some states communicated, major gaps remain |
| 3 | Good; most operations give clear feedback, minor gaps |
| 4 | Excellent; every action confirms, progress is always visible |
##### 2. Match Between System and Real World
Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
**Check for**:
- Familiar terminology (no unexplained jargon)
- Logical information order matching user expectations
- Recognizable icons and metaphors
- Domain-appropriate language for the target audience
- Natural reading flow (left-to-right, top-to-bottom priority)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Pure tech jargon, alien to users |
| 1 | Mostly confusing; requires domain expertise to navigate |
| 2 | Mixed; some plain language, some jargon leaks through |
| 3 | Mostly natural; occasional term needs context |
| 4 | Speaks the user's language fluently throughout |
##### 3. User Control and Freedom
Users need a clear "emergency exit" from unwanted states without extended dialogue.
**Check for**:
- Undo/redo functionality
- Cancel buttons on forms and modals
- Clear navigation back to safety (home, previous)
- Easy way to clear filters, search, selections
- Escape from long or multi-step processes
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Users get trapped; no way out without refreshing |
| 1 | Difficult exits; must find obscure paths to escape |
| 2 | Some exits; main flows have escape, edge cases don't |
| 3 | Good control; users can exit and undo most actions |
| 4 | Full control; undo, cancel, back, and escape everywhere |
##### 4. Consistency and Standards
Users shouldn't wonder whether different words, situations, or actions mean the same thing.
**Check for**:
- Consistent terminology throughout the interface
- Same actions produce same results everywhere
- Platform conventions followed (standard UI patterns)
- Visual consistency (colors, typography, spacing, components)
- Consistent interaction patterns (same gesture = same behavior)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Inconsistent everywhere; feels like different products stitched together |
| 1 | Many inconsistencies; similar things look/behave differently |
| 2 | Partially consistent; main flows match, details diverge |
| 3 | Mostly consistent; occasional deviation, nothing confusing |
| 4 | Fully consistent; cohesive system, predictable behavior |
##### 5. Error Prevention
Better than good error messages is a design that prevents problems in the first place.
**Check for**:
- Confirmation before destructive actions (delete, overwrite)
- Constraints preventing invalid input (date pickers, dropdowns)
- Smart defaults that reduce errors
- Clear labels that prevent misunderstanding
- Autosave and draft recovery
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Errors easy to make; no guardrails anywhere |
| 1 | Few safeguards; some inputs validated, most aren't |
| 2 | Partial prevention; common errors caught, edge cases slip |
| 3 | Good prevention; most error paths blocked proactively |
| 4 | Excellent; errors nearly impossible through smart constraints |
##### 6. Recognition Rather Than Recall
Minimize memory load. Make objects, actions, and options visible or easily retrievable.
**Check for**:
- Visible options (not buried in hidden menus)
- Contextual help when needed (tooltips, inline hints)
- Recent items and history
- Autocomplete and suggestions
- Labels on icons (not icon-only navigation)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Heavy memorization; users must remember paths and commands |
| 1 | Mostly recall; many hidden features, few visible cues |
| 2 | Some aids; main actions visible, secondary features hidden |
| 3 | Good recognition; most things discoverable, few memory demands |
| 4 | Everything discoverable; users never need to memorize |
##### 7. Flexibility and Efficiency of Use
Accelerators, invisible to novices, speed up expert interaction.
**Check for**:
- Keyboard shortcuts for common actions
- Customizable interface elements
- Recent items and favorites
- Bulk/batch actions
- Power user features that don't complicate the basics
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | One rigid path; no shortcuts or alternatives |
| 1 | Limited flexibility; few alternatives to the main path |
| 2 | Some shortcuts; basic keyboard support, limited bulk actions |
| 3 | Good accelerators; keyboard nav, some customization |
| 4 | Highly flexible; multiple paths, power features, customizable |
##### 8. Aesthetic and Minimalist Design
Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
**Check for**:
- Only necessary information visible at each step
- Clear visual hierarchy directing attention
- Purposeful use of color and emphasis
- No decorative clutter competing for attention
- Focused, uncluttered layouts
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Overwhelming; everything competes for attention equally |
| 1 | Cluttered; too much noise, hard to find what matters |
| 2 | Some clutter; main content clear, periphery noisy |
| 3 | Mostly clean; focused design, minor visual noise |
| 4 | Perfectly minimal; every element earns its pixel |
##### 9. Help Users Recognize, Diagnose, and Recover from Errors
Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
**Check for**:
- Plain language error messages (no error codes for users)
- Specific problem identification ("Email is missing @" not "Invalid input")
- Actionable recovery suggestions
- Errors displayed near the source of the problem
- Non-blocking error handling (don't wipe the form)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Cryptic errors; codes, jargon, or no message at all |
| 1 | Vague errors; "Something went wrong" with no guidance |
| 2 | Clear but unhelpful; names the problem but not the fix |
| 3 | Clear with suggestions; identifies problem and offers next steps |
| 4 | Perfect recovery; pinpoints issue, suggests fix, preserves user work |
##### 10. Help and Documentation
Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
**Check for**:
- Searchable help or documentation
- Contextual help (tooltips, inline hints, guided tours)
- Task-focused organization (not feature-organized)
- Concise, scannable content
- Easy access without leaving current context
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | No help available anywhere |
| 1 | Help exists but hard to find or irrelevant |
| 2 | Basic help; FAQ or docs exist, not contextual |
| 3 | Good documentation; searchable, mostly task-focused |
| 4 | Excellent contextual help; right info at the right moment |
---
#### Score Summary
**Total possible**: 40 points (10 heuristics × 4 max)
| Score Range | Rating | What It Means |
|-------------|--------|---------------|
| 3640 | Excellent | Minor polish only; ship it |
| 2835 | Good | Address weak areas, solid foundation |
| 2027 | Acceptable | Significant improvements needed before users are happy |
| 1219 | Poor | Major UX overhaul required; core experience broken |
| 011 | Critical | Redesign needed; unusable in current state |
---
#### Issue Severity (P0P3)
Tag each individual issue found during scoring with a priority level:
| Priority | Name | Description | Action |
|----------|------|-------------|--------|
| **P0** | Blocking | Prevents task completion entirely | Fix immediately; this is a showstopper |
| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.
---
### Persona-Based Design Testing
Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
**How to use**: Select 23 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags, not generic concerns.
---
#### 1. Impatient Power User: "Alex"
**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
**Behaviors**:
- Skips all onboarding and instructions
- Looks for keyboard shortcuts immediately
- Tries to bulk-select, batch-edit, and automate
- Gets frustrated by required steps that feel unnecessary
- Abandons if anything feels slow or patronizing
**Test Questions**:
- Can Alex complete the core task in under 60 seconds?
- Are there keyboard shortcuts for common actions?
- Can onboarding be skipped entirely?
- Do modals have keyboard dismiss (Esc)?
- Is there a "power user" path (shortcuts, bulk actions)?
**Red Flags** (report these specifically):
- Forced tutorials or unskippable onboarding
- No keyboard navigation for primary actions
- Slow animations that can't be skipped
- One-item-at-a-time workflows where batch would be natural
- Redundant confirmation steps for low-risk actions
---
#### 2. Confused First-Timer: "Jordan"
**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
**Behaviors**:
- Reads all instructions carefully
- Hesitates before clicking anything unfamiliar
- Looks for help or support constantly
- Misunderstands jargon and abbreviations
- Takes the most literal interpretation of any label
**Test Questions**:
- Is the first action obviously clear within 5 seconds?
- Are all icons labeled with text?
- Is there contextual help at decision points?
- Does terminology assume prior knowledge?
- Is there a clear "back" or "undo" at every step?
**Red Flags** (report these specifically):
- Icon-only navigation with no labels
- Technical jargon without explanation
- No visible help option or guidance
- Ambiguous next steps after completing an action
- No confirmation that an action succeeded
---
#### 3. Accessibility-Dependent User: "Sam"
**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
**Behaviors**:
- Tabs through the interface linearly
- Relies on ARIA labels and heading structure
- Cannot see hover states or visual-only indicators
- Needs adequate color contrast (4.5:1 minimum)
- May use browser zoom up to 200%
**Test Questions**:
- Can the entire primary flow be completed keyboard-only?
- Are all interactive elements focusable with visible focus indicators?
- Do images have meaningful alt text?
- Is color contrast WCAG AA compliant (4.5:1 for text)?
- Does the screen reader announce state changes (loading, success, errors)?
**Red Flags** (report these specifically):
- Click-only interactions with no keyboard alternative
- Missing or invisible focus indicators
- Meaning conveyed by color alone (red = error, green = success)
- Unlabeled form fields or buttons
- Time-limited actions without extension option
- Custom components that break screen reader flow
---
#### 4. Deliberate Stress Tester: "Riley"
**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
**Behaviors**:
- Tests edge cases intentionally (empty states, long strings, special characters)
- Submits forms with unexpected data (emoji, RTL text, very long values)
- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
- Looks for inconsistencies between what the UI promises and what actually happens
- Documents problems methodically
**Test Questions**:
- What happens at the edges (0 items, 1000 items, very long text)?
- Do error states recover gracefully or leave the UI in a broken state?
- What happens on refresh mid-workflow? Is state preserved?
- Are there features that appear to work but produce broken results?
- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
**Red Flags** (report these specifically):
- Features that appear to work but silently fail or produce wrong results
- Error handling that exposes technical details or leaves UI in a broken state
- Empty states that show nothing useful ("No results" with no guidance)
- Workflows that lose user data on refresh or navigation
- Inconsistent behavior between similar interactions in different parts of the UI
---
#### 5. Distracted Mobile User: "Casey"
**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
**Behaviors**:
- Uses thumb only; prefers bottom-of-screen actions
- Gets interrupted mid-flow and returns later
- Switches between apps frequently
- Has limited attention span and low patience
- Types as little as possible, prefers taps and selections
**Test Questions**:
- Are primary actions in the thumb zone (bottom half of screen)?
- Is state preserved if the user leaves and returns?
- Does it work on slow connections (3G)?
- Can forms use autocomplete and smart defaults?
- Are touch targets at least 44×44pt?
**Red Flags** (report these specifically):
- Important actions positioned at the top of the screen (unreachable by thumb)
- No state persistence; progress lost on tab switch or interruption
- Large text inputs required where selection would work
- Heavy assets loading on every page (no lazy loading)
- Tiny tap targets or targets too close together
---
#### Selecting Personas
Choose personas based on the interface type:
| Interface Type | Primary Personas | Why |
|---------------|-----------------|-----|
| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
| Dashboard / admin | Alex, Sam | Power users, accessibility |
| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
| Onboarding flow | Jordan, Casey | Confusion, interruption |
| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
---
#### Project-Specific Personas
If `{{config_file}}` contains a `## Design Context` section (generated by `impeccable init`), derive 12 additional personas from the audience and brand information:
1. Read the target audience description
2. Identify the primary user archetype not covered by the 5 predefined personas
3. Create a persona following this template:
```
##### [Role]: "[Name]"
**Profile**: [2-3 key characteristics derived from Design Context]
**Behaviors**: [3-4 specific behaviors based on the described audience]
**Red Flags**: [3-4 things that would alienate this specific user type]
```
Only generate project-specific personas when real Design Context data is available. Don't invent audience details; use the 5 predefined personas when no context exists.
+11 -9
View File
@@ -45,7 +45,7 @@ Rules that matter:
- **Token refs** use `{path.to.token}` (e.g. `{colors.primary}`, `{rounded.md}`). Components may reference primitives; primitives may not reference each other.
- **Stitch validates colors as hex sRGB only** (`#RGB` / `#RGBA` / `#RRGGBB` / `#RRGGBBAA`); OKLCH/HSL/P3 trigger a linter warning, not a hard error. YAML accepts the string either way and our own parser is format-agnostic. Choose based on project posture: (a) if the project has an "OKLCH-only" doctrine or uses Display-P3 values that don't round-trip through sRGB, put OKLCH directly in the frontmatter and accept the Stitch linter warning; (b) if the project wants strict Stitch compliance or plans to use their Tailwind/DTCG export pipeline, put hex in the frontmatter and keep OKLCH in prose as the canonical reference. Never split the source of truth without explicit reason.
- **Component sub-tokens** are limited to 8 props: `backgroundColor`, `textColor`, `typography`, `rounded`, `padding`, `size`, `height`, `width`. Shadows, motion, focus rings, backdrop-filter: none of those fit. Carry them in the sidecar (Step 4b).
- **Scale keys are open-ended.** Use whatever names the project already uses (`warm-ash-cream`, `surface-container-low`). Don't rename to Material defaults.
- **Scale keys are open-ended.** Use whatever names the project already uses (`oxblood-deep`, `surface-container-low`). Don't rename to Material defaults.
- **Variants are naming convention, not schema.** `button-primary` / `button-primary-hover` / `button-primary-active` as sibling keys.
## The markdown body: six sections (exact order)
@@ -61,7 +61,7 @@ Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] P
## When to run
- The user just ran `/impeccable teach` and needs the visual side documented.
- The user just ran `/impeccable init` and needs the visual side documented.
- The skill noticed no `DESIGN.md` exists and nudged the user to create one.
- An existing `DESIGN.md` is stale (the design has drifted).
- Before a large redesign, to capture the current state as a reference.
@@ -71,7 +71,7 @@ If a `DESIGN.md` already exists, **do not silently overwrite it**. Show the user
## Two paths
- **Scan mode** (default): the project has design tokens, components, or rendered output. Extract, then confirm descriptive language. Use when there's code to analyze.
- **Seed mode**: the project is pre-implementation (fresh teach, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked `<!-- SEED -->`. Re-run in scan mode once there's code.
- **Seed mode**: the project is pre-implementation (fresh init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked `<!-- SEED -->`. Re-run in scan mode once there's code.
Decide by scanning first (Scan mode Step 1). If the scan finds no tokens, no component files, and no rendered site, offer seed mode; don't silently switch. `/impeccable document --seed` forces seed mode regardless of code presence.
@@ -103,7 +103,7 @@ Build a structured draft from the discovered tokens. For each token class:
From the auto-extracted tokens, draft the YAML frontmatter now (you'll write it at the top of DESIGN.md in Step 4). This is the machine-readable layer: what the live panel and Stitch's linter consume.
- **Colors**: one entry per extracted color. Key = descriptive slug (`warm-ash-cream`, `editorial-magenta`, not `blue-800`). Value = whichever format the project treats as canonical (OKLCH or hex; see the frontmatter rules above). Don't split the source of truth: one format in the frontmatter, don't redefine the same token in prose with a different value.
- **Colors**: one entry per extracted color. Key = descriptive slug (`oxblood-deep`, `editorial-magenta`, not `blue-800`). Value = whichever format the project treats as canonical (OKLCH or hex; see the frontmatter rules above). Don't split the source of truth: one format in the frontmatter, don't redefine the same token in prose with a different value.
- **Typography**: one entry per role (`display`, `headline`, `title`, `body`, `label`). Typography is an object; include only the props that are real for the project (`fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, `letterSpacing`, `fontFeature`, `fontVariation`).
- **Rounded / Spacing**: whatever scale steps the project actually uses, keyed by whatever scale name the project uses (`sm` / `md` / `lg`, or `surface-sm`, or numeric steps).
- **Components**: one entry per variant (`button-primary`, `button-primary-hover`, `button-ghost`). Reference primitives via `{colors.X}`, `{rounded.Y}`. If a variant needs a property Stitch's 8-prop set doesn't cover (shadow, focus ring, backdrop-filter), carry the full snippet in the sidecar instead.
@@ -253,7 +253,7 @@ Regenerate the sidecar whenever you regenerate root `DESIGN.md`. If the user onl
"extensions": {
"colorMeta": {
"primary": { "role": "primary", "displayName": "Editorial Magenta", "canonical": "oklch(60% 0.25 350)", "tonalRamp": ["...", "...", "..."] },
"warm-ash-cream": { "role": "neutral", "displayName": "Warm Ash Cream", "canonical": "oklch(96% 0.005 350)", "tonalRamp": ["...", "...", "..."] }
"cool-paper": { "role": "neutral", "displayName": "Cool Paper", "canonical": "oklch(96% 0.005 230)", "tonalRamp": ["...", "...", "..."] }
},
"typographyMeta": {
"display": { "displayName": "Display", "purpose": "Hero headlines only." }
@@ -328,12 +328,13 @@ Pull directly from the DESIGN.md you just wrote:
Do not reword. The panel shows these as secondary collapsible context; the same voice that's in the Markdown carries through.
### Step 5: Confirm, refine, and refresh session cache
### Step 5: Confirm and refine
1. Show the user the full DESIGN.md you wrote. Briefly highlight the non-obvious creative choices (descriptive color names, atmosphere language, named rules).
2. Mention that `.impeccable/design.json` was also written alongside; the live panel will now render this project's actual button/input/nav primitives instead of generic approximations.
3. Offer to refine any section: "Want me to revise a section, add component patterns I missed, or adjust the atmosphere language?"
4. **Refresh the session cache.** Run `node {{scripts_path}}/load-context.mjs` one final time so the newly-written DESIGN.md lands in conversation. Subsequent commands in this session will use the fresh version automatically without re-reading.
Your own write is the freshest source; subsequent commands in this session don't need a reload.
## Seed mode
@@ -394,11 +395,12 @@ Per-section guidance in seed mode:
Seed mode writes a minimal frontmatter with `name` and `description` only; no colors, typography, rounded, spacing, or components yet. Real tokens land on the next Scan-mode run. Skip the `.impeccable/design.json` sidecar in seed mode for the same reason: nothing to render.
### Step 4: Confirm and refresh session cache
### Step 4: Confirm
1. Show the seed DESIGN.md. Call out that it is a seed (the marker is the literal commitment).
2. Tell the user: "Re-run `/impeccable document` once you have some code. That pass will extract real tokens and generate the sidecar."
3. Run `node {{scripts_path}}/load-context.mjs` once so the seed lands in conversation for the rest of the session.
Your own write is the freshest source; no reload needed.
## Style guidelines
-234
View File
@@ -1,234 +0,0 @@
# Heuristics Scoring Guide
Score each of Nielsen's 10 Usability Heuristics on a 04 scale. Be honest: a 4 means genuinely excellent, not "good enough."
## Nielsen's 10 Heuristics
### 1. Visibility of System Status
Keep users informed about what's happening through timely, appropriate feedback.
**Check for**:
- Loading indicators during async operations
- Confirmation of user actions (save, submit, delete)
- Progress indicators for multi-step processes
- Current location in navigation (breadcrumbs, active states)
- Form validation feedback (inline, not just on submit)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | No feedback; user is guessing what happened |
| 1 | Rare feedback; most actions produce no visible response |
| 2 | Partial; some states communicated, major gaps remain |
| 3 | Good; most operations give clear feedback, minor gaps |
| 4 | Excellent; every action confirms, progress is always visible |
### 2. Match Between System and Real World
Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
**Check for**:
- Familiar terminology (no unexplained jargon)
- Logical information order matching user expectations
- Recognizable icons and metaphors
- Domain-appropriate language for the target audience
- Natural reading flow (left-to-right, top-to-bottom priority)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Pure tech jargon, alien to users |
| 1 | Mostly confusing; requires domain expertise to navigate |
| 2 | Mixed; some plain language, some jargon leaks through |
| 3 | Mostly natural; occasional term needs context |
| 4 | Speaks the user's language fluently throughout |
### 3. User Control and Freedom
Users need a clear "emergency exit" from unwanted states without extended dialogue.
**Check for**:
- Undo/redo functionality
- Cancel buttons on forms and modals
- Clear navigation back to safety (home, previous)
- Easy way to clear filters, search, selections
- Escape from long or multi-step processes
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Users get trapped; no way out without refreshing |
| 1 | Difficult exits; must find obscure paths to escape |
| 2 | Some exits; main flows have escape, edge cases don't |
| 3 | Good control; users can exit and undo most actions |
| 4 | Full control; undo, cancel, back, and escape everywhere |
### 4. Consistency and Standards
Users shouldn't wonder whether different words, situations, or actions mean the same thing.
**Check for**:
- Consistent terminology throughout the interface
- Same actions produce same results everywhere
- Platform conventions followed (standard UI patterns)
- Visual consistency (colors, typography, spacing, components)
- Consistent interaction patterns (same gesture = same behavior)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Inconsistent everywhere; feels like different products stitched together |
| 1 | Many inconsistencies; similar things look/behave differently |
| 2 | Partially consistent; main flows match, details diverge |
| 3 | Mostly consistent; occasional deviation, nothing confusing |
| 4 | Fully consistent; cohesive system, predictable behavior |
### 5. Error Prevention
Better than good error messages is a design that prevents problems in the first place.
**Check for**:
- Confirmation before destructive actions (delete, overwrite)
- Constraints preventing invalid input (date pickers, dropdowns)
- Smart defaults that reduce errors
- Clear labels that prevent misunderstanding
- Autosave and draft recovery
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Errors easy to make; no guardrails anywhere |
| 1 | Few safeguards; some inputs validated, most aren't |
| 2 | Partial prevention; common errors caught, edge cases slip |
| 3 | Good prevention; most error paths blocked proactively |
| 4 | Excellent; errors nearly impossible through smart constraints |
### 6. Recognition Rather Than Recall
Minimize memory load. Make objects, actions, and options visible or easily retrievable.
**Check for**:
- Visible options (not buried in hidden menus)
- Contextual help when needed (tooltips, inline hints)
- Recent items and history
- Autocomplete and suggestions
- Labels on icons (not icon-only navigation)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Heavy memorization; users must remember paths and commands |
| 1 | Mostly recall; many hidden features, few visible cues |
| 2 | Some aids; main actions visible, secondary features hidden |
| 3 | Good recognition; most things discoverable, few memory demands |
| 4 | Everything discoverable; users never need to memorize |
### 7. Flexibility and Efficiency of Use
Accelerators, invisible to novices, speed up expert interaction.
**Check for**:
- Keyboard shortcuts for common actions
- Customizable interface elements
- Recent items and favorites
- Bulk/batch actions
- Power user features that don't complicate the basics
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | One rigid path; no shortcuts or alternatives |
| 1 | Limited flexibility; few alternatives to the main path |
| 2 | Some shortcuts; basic keyboard support, limited bulk actions |
| 3 | Good accelerators; keyboard nav, some customization |
| 4 | Highly flexible; multiple paths, power features, customizable |
### 8. Aesthetic and Minimalist Design
Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
**Check for**:
- Only necessary information visible at each step
- Clear visual hierarchy directing attention
- Purposeful use of color and emphasis
- No decorative clutter competing for attention
- Focused, uncluttered layouts
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Overwhelming; everything competes for attention equally |
| 1 | Cluttered; too much noise, hard to find what matters |
| 2 | Some clutter; main content clear, periphery noisy |
| 3 | Mostly clean; focused design, minor visual noise |
| 4 | Perfectly minimal; every element earns its pixel |
### 9. Help Users Recognize, Diagnose, and Recover from Errors
Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
**Check for**:
- Plain language error messages (no error codes for users)
- Specific problem identification ("Email is missing @" not "Invalid input")
- Actionable recovery suggestions
- Errors displayed near the source of the problem
- Non-blocking error handling (don't wipe the form)
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | Cryptic errors; codes, jargon, or no message at all |
| 1 | Vague errors; "Something went wrong" with no guidance |
| 2 | Clear but unhelpful; names the problem but not the fix |
| 3 | Clear with suggestions; identifies problem and offers next steps |
| 4 | Perfect recovery; pinpoints issue, suggests fix, preserves user work |
### 10. Help and Documentation
Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
**Check for**:
- Searchable help or documentation
- Contextual help (tooltips, inline hints, guided tours)
- Task-focused organization (not feature-organized)
- Concise, scannable content
- Easy access without leaving current context
**Scoring**:
| Score | Criteria |
|-------|----------|
| 0 | No help available anywhere |
| 1 | Help exists but hard to find or irrelevant |
| 2 | Basic help; FAQ or docs exist, not contextual |
| 3 | Good documentation; searchable, mostly task-focused |
| 4 | Excellent contextual help; right info at the right moment |
---
## Score Summary
**Total possible**: 40 points (10 heuristics × 4 max)
| Score Range | Rating | What It Means |
|-------------|--------|---------------|
| 3640 | Excellent | Minor polish only; ship it |
| 2835 | Good | Address weak areas, solid foundation |
| 2027 | Acceptable | Significant improvements needed before users are happy |
| 1219 | Poor | Major UX overhaul required; core experience broken |
| 011 | Critical | Redesign needed; unusable in current state |
---
## Issue Severity (P0P3)
Tag each individual issue found during scoring with a priority level:
| Priority | Name | Description | Action |
|----------|------|-------------|--------|
| **P0** | Blocking | Prevents task completion entirely | Fix immediately; this is a showstopper |
| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.
@@ -1,21 +1,16 @@
# Teach Flow
# Init Flow
Gathers design context for a project and writes two complementary files at the project root:
The setup command for a project. One codebase crawl feeds everything it writes:
- **PRODUCT.md** (strategic): root project file for register, target users, product purpose, brand personality, anti-references, strategic design principles. Answers "who/what/why".
- **DESIGN.md** (visual): root project file for visual theme, color palette, typography, components, layout. Follows the [Google Stitch DESIGN.md format](https://stitch.withgoogle.com/docs/design-md/format/). Answers "how it looks".
- **`.impeccable/live/config.json`** (live mode): pre-configured so `{{command_prefix}}impeccable live` boots straight into variant mode with no first-time detour.
Every other impeccable command reads these files before doing any work.
It closes by pointing the user at the best command to run next. Every other impeccable command reads PRODUCT.md and DESIGN.md before doing any work.
## Step 1: Load current state
Run the shared loader first so you know what already exists:
```bash
node {{scripts_path}}/load-context.mjs
```
The output tells you whether PRODUCT.md and/or DESIGN.md already exist. If `migrated: true`, legacy `.impeccable.md` was auto-renamed to `PRODUCT.md`. Mention this once to the user.
Check what already exists. PRODUCT.md and DESIGN.md live at the project root, or under `.agents/context/` or `docs/` (case-insensitive). Read whichever are present with your native file tool. Also note whether `.impeccable/live/config.json` already exists (Step 6 leaves it untouched if so).
Decision tree:
- **Neither file exists (empty project or no context yet)**: do Steps 2-4 (write PRODUCT.md), then decide on DESIGN.md based on whether there's code to analyze.
@@ -26,14 +21,14 @@ Decision tree:
Never silently overwrite an existing file. Always confirm first.
If teach was invoked as a setup blocker by another command, such as `{{command_prefix}}impeccable craft landing page`, pause that command here. Complete teach, re-run the loader, then resume the original command with the freshly loaded context. For craft, resume into shape next; teach creates project context, but it is not a substitute for the task-specific shape interview and confirmed design brief.
If init was invoked as a setup blocker by another command, such as `{{command_prefix}}impeccable craft landing page`, pause that command here. Complete init, then resume the original command. Your own writes are the freshest source; no reload needed. For craft, resume into shape next; init creates project context, but it is not a substitute for the task-specific shape interview and confirmed design brief.
## Step 2: Explore the codebase
Before asking questions, thoroughly scan the project to discover what you can:
Before asking questions, thoroughly scan the project to discover what you can. This single crawl feeds PRODUCT.md, DESIGN.md, **and** the live-mode framework detection in Step 6, so be thorough once rather than re-scanning later:
- **README and docs**: Project purpose, target audience, any stated goals
- **Package.json / config files**: Tech stack, dependencies, existing design libraries
- **Package.json / config files**: Tech stack, dependencies, existing design libraries, **and the framework** (Vite/SPA, Next.js, Nuxt, SvelteKit, Astro, multi-page static) plus the HTML entry the browser actually loads
- **Existing components**: Current design patterns, spacing, typography in use
- **Brand assets**: Logos, favicons, color values already defined
- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
@@ -46,7 +41,7 @@ Also form a **register hypothesis** from what you find:
Register is a hypothesis at this point, not a decision; Step 3 confirms it.
Note what you've learned and what remains unclear. This exploration feeds both PRODUCT.md and DESIGN.md.
Note what you've learned and what remains unclear. Also note any rough edges worth a follow-up command (thin hierarchy, flat or gray palette, missing error/empty states, dull copy); Step 7 turns these into concrete recommendations without re-analyzing.
## Step 3: Ask strategic questions (for PRODUCT.md)
@@ -84,8 +79,7 @@ If the signal is genuinely split (e.g. a product with a big marketing landing),
### Brand & Personality
- How would you describe the brand personality in 3 words?
- Reference sites or apps that capture the right feel? What specifically about them?
- For brand, push for real-world references in the right lane (tech-minimal, editorial-magazine, consumer-warm, brutalist-grid, etc.), not generic "modern" adjectives.
- For product, push for category best-tool references (Linear, Figma, Notion, Raycast, Stripe).
- Push for specific named references with the *specific* thing about them that fits this brand, not generic "modern" adjectives or category-bucket lanes.
- What should this explicitly NOT look like? Any anti-references?
### Accessibility & Inclusion
@@ -141,16 +135,38 @@ If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs
If the user prefers to skip, mention they can run `/impeccable document` any time later.
## Step 6: Confirm and wrap up
## Step 6: Configure live mode (when code exists)
Summarize:
If the project has code with HTML entries and a dev server (the same "code exists" condition that puts `/impeccable document` in scan mode), pre-configure live mode now. You already identified the framework and the served HTML entry in Step 2, so this is nearly free, and it spares the user the first-time setup detour when they later run `/impeccable live`.
**Skip this step for empty / pre-implementation projects** (nothing to inject into yet). Tell the user live mode will configure itself the first time they run it once there's code.
**If `.impeccable/live/config.json` already exists, leave it untouched** and note that live mode is already configured.
Otherwise:
1. Write `.impeccable/live/config.json`. Choose `files` (the HTML entries the browser actually loads), `insertBefore`, and `commentSyntax` from the framework table in [live.md](live.md)'s **First-time setup** section, using the framework you found in Step 2. That table is canonical; do not restate it here. For multi-page static sites, prefer a glob (`["public/**/*.html"]`) over a literal list.
2. Run `node {{scripts_path}}/detect-csp.mjs`. If it reports a patchable shape (`append-arrays` / `append-string`), use the **consent prompt template** from live.md before editing any source file. On decline, skip the patch. For `middleware` / `meta-tag` shapes, surface the detected files and ask the user to add `http://localhost:8400` to `script-src` and `connect-src` manually. For `null`, there's nothing to do.
3. Set `cspChecked: true` in the config once CSP is handled (patched, declined, manual, or not needed). The schema and per-shape patch details live in live.md's First-time setup; follow it rather than duplicating.
Writing the config file is harmless and needs no consent; only the CSP **source-file patch** requires a yes.
## Step 7: Recommend starting points, then wrap up
Summarize tersely:
- Register captured (brand / product)
- What was written (PRODUCT.md, DESIGN.md, or both)
- What was written (PRODUCT.md, DESIGN.md, live config, or a subset)
- The 3-5 strategic principles from PRODUCT.md that will guide future work
- If DESIGN.md is pending, remind the user how to generate it later
- If DESIGN.md or live config is pending, one line on how to set it up later
**Critical: re-run the loader to refresh session context.** After writing PRODUCT.md, run `node {{scripts_path}}/load-context.mjs` one final time and let its full JSON output land in conversation. This ensures subsequent commands in this session use the freshly-written PRODUCT.md, not a stale earlier version.
Then recommend the **best commands to run next**, drawn from what your Step 2 crawl already surfaced. Do not run a fresh analysis here; surface observations you already have. Tailor to register and to what you saw, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent:
If teach was invoked as a blocker by another impeccable command (e.g. the user ran `/impeccable polish` with no PRODUCT.md), resume that original task now with the fresh context.
- **Build something new**: `/impeccable craft <feature>` (shape, then build end-to-end) or `/impeccable shape <feature>` (plan first). Lead with this for empty or early-stage projects.
- **Improve what's there**: name the specific surface. `/impeccable critique <page>` for a scored UX review; `/impeccable audit <area>` for a11y / perf / responsive checks; `/impeccable polish <component>` for a pre-ship pass. When the crawl flagged a specific weakness, point the matching command at it: thin hierarchy or spacing → `layout`, flat or gray palette → `colorize`, missing error / empty states → `harden` or `onboard`, dull or unclear copy → `clarify`.
- **Iterate visually**: `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place.
The full command menu is one bare `/impeccable` away; keep this list short and pointed.
If init was invoked as a blocker by another impeccable command (e.g. the user ran `/impeccable polish` with no PRODUCT.md), resume that original task now. Your own writes are the freshest source; no reload needed.
Optionally {{ask_instruction}} Ask whether they'd like a brief summary of PRODUCT.md appended to {{config_file}} for easier agent reference. If yes, append a short **Design Context** pointer section there.
-6
View File
@@ -150,12 +150,6 @@ For browsers without anchor positioning support, `position: fixed` with manual c
Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
### Anti-Patterns
- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
## Destructive Actions: Undo > Confirm
**Undo is better than confirmation dialogs.** Users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+32 -12
View File
@@ -1,4 +1,4 @@
Space is the most underused design tool. Find the layout's actual problem (monotone spacing, weak hierarchy, identical card grids, the centered-stack default) and fix the structure, not the surface.
Space is the most underused design tool. Find the layout's actual problem (monotone spacing, weak hierarchy, identical card grids) and fix the structure, not the surface.
---
@@ -27,7 +27,6 @@ Analyze what's weak about the current spatial design:
3. **Grid & structure**:
- Is there a clear underlying structure, or does the layout feel random?
- Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
- Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
4. **Rhythm & variety**:
- Does the layout have visual rhythm? (Alternating tight/generous spacing)
@@ -43,8 +42,6 @@ Analyze what's weak about the current spatial design:
## Plan Layout Improvements
Consult the [spatial design reference](spatial-design.md) for detailed guidance on grids, rhythm, and container queries.
Create a systematic plan:
- **Spacing system**: Use a consistent scale (a framework's built-in scale like Tailwind's, rem-based tokens, or a custom system). The specific values matter less than consistency.
@@ -57,6 +54,7 @@ Create a systematic plan:
### Establish a Spacing System
- Use a consistent spacing scale (framework scales like Tailwind, rem-based tokens, or a custom scale all work). What matters is that values come from a defined set, not arbitrary numbers.
- Prefer a 4pt base scale (4, 8, 12, 16, 24, 32, 48, 64, 96px) over 8pt; 8pt is too coarse and you'll frequently need 12px between 8 and 16.
- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
- Use `gap` for sibling spacing instead of margins; eliminates margin collapse hacks
- Apply `clamp()` for fluid spacing that breathes on larger screens
@@ -66,15 +64,22 @@ Create a systematic plan:
- **Tight grouping** for related elements (8-12px between siblings)
- **Generous separation** between distinct sections (48-96px)
- **Varied spacing** within sections (not every row needs the same gap)
- **Asymmetric compositions**: break the predictable centered-content pattern when it makes sense
- **Asymmetric compositions**: a deliberate choice when the content invites it (not a default to chase).
### Choose the Right Layout Tool
- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals.
- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
- Use named grid areas (`grid-template-areas`) for complex page layouts; redefine at breakpoints.
- Use **container queries** for components, viewport queries for page layouts. A card in a narrow sidebar can stay compact while the same card in a main content area expands automatically:
```css
.card-container { container-type: inline-size; }
.card { display: grid; gap: var(--space-md); }
@container (min-width: 400px) {
.card { grid-template-columns: 120px 1fr; }
}
```
### Break Card Grid Monotony
@@ -85,18 +90,36 @@ Create a systematic plan:
### Strengthen Visual Hierarchy
- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough; generous whitespace around an element draws the eye. Some of the most polished designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
- The best hierarchy combines 23 dimensions at once. A heading that's larger, bolder, AND has more space above it reads as primary without trying:
| Tool | Strong Hierarchy | Weak Hierarchy |
|------|------------------|----------------|
| **Size** | 3:1 ratio or more | <2:1 ratio |
| **Weight** | Bold vs Regular | Medium vs Regular |
| **Color** | High contrast | Similar tones |
| **Position** | Top/left (primary) | Bottom/right |
| **Space** | Surrounded by white space | Crowded |
- Be aware of reading flow: in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
- Create clear content groupings through proximity and separation.
### Manage Depth & Elevation
- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
- Build a consistent shadow scale (sm → md → lg → xl); shadows should be subtle
- Use elevation to reinforce hierarchy, not as decoration
### Optical Adjustments
- If an icon looks visually off-center despite being geometrically centered, nudge it. But only if you're confident it actually looks wrong. Don't adjust speculatively.
- Text at `margin-left: 0` looks slightly indented because of letterform whitespace; a negative margin (`-0.05em`) optically aligns it. Geometrically centered glyphs often look off-center (play icons need to shift right, arrows shift toward their direction).
- Touch targets must be 44×44px minimum even when the visual element is smaller. Expand the hit area with padding or a pseudo-element:
```css
.icon-button { width: 24px; height: 24px; position: relative; }
.icon-button::before {
content: ''; position: absolute; inset: -10px;
}
```
**NEVER**:
- Use arbitrary spacing values outside your scale
@@ -104,10 +127,7 @@ Create a systematic plan:
- Wrap everything in cards (not everything needs a container)
- Nest cards inside cards (use spacing and dividers for hierarchy within)
- Use identical card grids everywhere (icon + heading + text, repeated)
- Center everything (left-aligned with asymmetry feels more designed)
- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work, but it should display actual data, not decorative numbers.
- Default to CSS Grid when Flexbox would be simpler; use the simplest tool for the job
- Use arbitrary z-index values (999, 9999); build a semantic scale
## Verify Layout Improvements
+74 -9
View File
@@ -4,23 +4,30 @@ Interactive live variant mode: select elements in the browser, pick a design act
A running dev server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser.
<codex>
Codex: run live helper commands, the app dev server, and any dependency-installing setup with `sandbox_permissions: "require_escalated"` from the start; live mode depends on localhost and package-manager network access that the sandbox blocks.
</codex>
## The contract (read once)
Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot.
2. Navigate to the URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). If you can't infer it confidently, tell the user once to open their dev/preview URL. Never use `serverPort` as that URL; it's the helper, not the app.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). After every event or `--reply`, run `live-poll.mjs` again immediately. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: read screenshot if present; load the action's reference; plan three distinct directions; write all variants in one edit; `--reply done`; poll again.
5. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately; carbonize accepts remain recoverable until you finish cleanup, run `live-complete.mjs --id EVENT_ID`, and only then poll again.
6. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
7. On `exit`: run the cleanup at the bottom.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately; carbonize accepts remain recoverable until you finish cleanup, run `live-complete.mjs --id EVENT_ID`, and only then poll again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run the poll in the **foreground** (blocking shell; not a background terminal, not a subagent). Cursor background terminals and subagents do not reliably resume the chat with poll stdout.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: run the poll in the **foreground** (blocking shell; not a background task, not a subagent). Codex background exec sessions do not reliably surface poll stdout back into the conversation at the moment events arrive, so a "fire-and-forget" background poll will stall live mode.
- **Other harnesses**: foreground unless you know stdout reliably returns to this session.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodies. Spend tokens on tools and edits; on failure, one or two short sentences.
@@ -30,7 +37,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi
node {{scripts_path}}/live.mjs
```
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath, migrated }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt. If `migrated: true`, the loader auto-renamed legacy `.impeccable.md` to `PRODUCT.md`; mention this once and suggest `/impeccable document` for the matching DESIGN.md.
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -38,12 +45,15 @@ If output is `{ ok: false, error: "config_missing" | "config_invalid", path }`,
## Poll loop
**Default (portable, all harnesses):**
```
LOOP:
node {{scripts_path}}/live-poll.mjs # default long timeout; no --timeout=
Read JSON; dispatch on "type"
"generate" → Handle Generate; reply done; LOOP
"steer" → Handle Steer; reply steer_done; LOOP
"accept" → Handle Accept; complete carbonize cleanup if required; LOOP
"discard" → Handle Discard; LOOP
"prefetch" → Handle Prefetch; LOOP
@@ -51,6 +61,16 @@ LOOP:
"exit" → break → Cleanup
```
**Stream mode (experimental, not for Cursor):**
```
node {{scripts_path}}/live-poll.mjs --stream # stays running; one JSON line per event
Handle event; run --reply in a separate command
Repeat until "exit" line → Cleanup
```
Stream keeps one process alive and waits for `--reply` ack before polling again. Useful only when the harness reads incremental stdout reliably and quickly. **Cursor is not one of those:** background pattern notify on a long-running shell was ~5s to pick up events vs sub-second for one-shot exit notify. Default to one-shot everywhere unless you have measured otherwise.
## Recovery commands
The live helper persists an append-only journal under `.impeccable/live/sessions/`. Browser checkpoints are advisory but durable; the journal is canonical. This is local durable recovery state, not project source.
@@ -71,9 +91,32 @@ Server restart rule: start `live-server.mjs` again, then poll. Startup requeues
## Handle `generate`
Event: `{id, action, freeformPrompt?, count, pageUrl, element, screenshotPath?, comments?, strokes?}`.
**Replace mode** (default): `{id, action, freeformPrompt?, count, pageUrl, element, screenshotPath?, comments?, strokes?}`.
Speed matters; the user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
**Insert mode** (`event.mode === "insert"`): `{id, mode: "insert", count, pageUrl, insert: { position, anchor }, placeholder: { width, height }, freeformPrompt?, screenshotPath?, comments?, strokes?}`. No `action`. Requires a non-empty `freeformPrompt` **or** annotations. Screenshot is sent only when annotations exist (same rule as replace). Use `placeholder` dimensions as a soft size hint for net-new content.
Speed matters; the user is watching a spinner. Minimize tool calls by using the wrap/insert helper and writing all variants in a single edit.
### Insert mode branch
When `event.mode === "insert"`:
1. Read the screenshot if `event.screenshotPath` is present (annotations only).
2. Run the insert helper instead of wrap:
```bash
node {{scripts_path}}/live-insert.mjs --id EVENT_ID --count EVENT_COUNT --position after \
--element-id "ANCHOR_ID" --classes "class1,class2" --tag "section" --text "ANCHOR_TEXT"
```
- `--position``event.insert.position` (`before` | `after`)
- Anchor flags ← `event.insert.anchor` (same mapping as wrap: id, classes, tag, text)
The scaffold has **no** `data-impeccable-variant="original"`. Variants are net-new HTML+CSS inserted at `insertLine`. Load `brand.md` or `product.md` (freeform only, no action sub-command). Write all variants in one edit, then `--reply done`.
On accept/discard, `live-accept.mjs` removes the wrapper block; the anchor element is untouched.
### Replace mode (default)
### 1. Read the screenshot (if present)
@@ -424,6 +467,28 @@ A background agent may be used for the rewrite, but the current thread is respon
Event: `{id, _acceptResult, _completionAck}`. The poll script already restored the original, removed all variant markers, and acknowledged `discarded` durable completion. Nothing to do unless `_completionAck.ok !== true`; in that case run `live-complete.mjs --id EVENT_ID --discarded`, then poll again.
## Handle `steer`
Event: `{id, message, pageUrl}`. The user typed or spoke into the global bar **Steer** control: page-level direction without picking an element or launching variant generation.
The mic button uses the browser **Web Speech API** (MVP): click to start, speak, stop automatically when the utterance ends, then the transcript submits as a steer event. Click again while listening to cancel without submitting.
This is lighter than `generate`: no screenshot, no element context, no variant cycling. Read `message` and inspect the live page or project files as needed, then either make edits or answer in prose.
When finished:
```bash
node {{scripts_path}}/live-poll.mjs --reply EVENT_ID steer_done ["Optional short note for a browser toast"]
```
On failure:
```bash
node {{scripts_path}}/live-poll.mjs --reply EVENT_ID error "Short reason"
```
Then poll again immediately. Do not send a separate "picked up" reply. The Steer bar stays locked until `steer_done` or `error` arrives over SSE.
## Handle `prefetch`
Event: `{pageUrl}`. The browser fires this the first time the user selects an element on a given route, as a latency shortcut; it signals the user is likely about to Go on a page you haven't read yet.
-109
View File
@@ -1,109 +0,0 @@
# Motion Design
## Duration: The 100/300/500 Rule
Timing matters more than easing. These durations feel right for most UI:
| Duration | Use Case | Examples |
|----------|----------|----------|
| **100-150ms** | Instant feedback | Button press, toggle, color change |
| **200-300ms** | State changes | Menu open, tooltip, hover states |
| **300-500ms** | Layout changes | Accordion, modal, drawer |
| **500-800ms** | Entrance animations | Page load, hero reveals |
**Exit animations are faster than entrances.** Use ~75% of enter duration.
## Easing: Pick the Right Curve
**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
| Curve | Use For | CSS |
|-------|---------|-----|
| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
**For micro-interactions, use exponential curves.** They feel natural because they mimic real physics (friction, deceleration):
```css
/* Quart out - smooth, refined (recommended default) */
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
/* Quint out - slightly more dramatic */
--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
/* Expo out - snappy, confident */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
```
**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop; they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
## Premium Motion Materials
Transform and opacity are reliable defaults, not the whole palette. Premium interfaces often need atmospheric properties: blur reveals, backdrop-filter panels, saturation or brightness shifts, shadow bloom, SVG filters, masks, clip paths, gradient-position movement, and variable font or shader-driven effects.
Use the right material for the effect:
- **Transform / opacity**: movement, press feedback, simple reveals, list choreography.
- **Blur / filter / backdrop-filter**: focus pulls, depth, glass or lens effects, softened entrances, atmospheric transitions.
- **Clip path / masks**: wipes, reveals, editorial cropping, product-like transitions.
- **Shadow / glow / color filters**: energy, affordance, focus, warmth, active state.
- **Grid-template rows or FLIP-style transforms**: expanding and reflowing layout without animating `height` directly.
The hard rule is not "transform and opacity only." The hard rule is: avoid animating layout-driving properties casually (`width`, `height`, `top`, `left`, margins), keep expensive effects bounded to small or isolated areas, and verify in-browser that the result is smooth on the target viewports. If blur/filter makes the interaction feel significantly more premium and remains smooth, use it.
## Staggered Animations
Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**: 10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
## Reduced Motion
This is not optional. Vestibular disorders affect ~35% of adults over 40.
```css
/* Define animations normally */
.card {
animation: slide-up 500ms ease-out;
}
/* Provide alternative for reduced motion */
@media (prefers-reduced-motion: reduce) {
.card {
animation: fade-in 200ms ease-out; /* Crossfade instead of motion */
}
}
/* Or disable entirely */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
```
**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work, just without spatial movement.
## Perceived Performance
**Nobody cares how fast your site is, just how fast it feels.** Perception can be as effective as actual performance.
**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
- **Early completion**: Show content progressively, don't wait for everything. Video buffering, progressive images, streaming HTML.
- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline; the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
## Performance
Don't use `will-change` preemptively, only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
---
**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.
-179
View File
@@ -1,179 +0,0 @@
# Persona-Based Design Testing
Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
**How to use**: Select 23 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags, not generic concerns.
---
## 1. Impatient Power User: "Alex"
**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
**Behaviors**:
- Skips all onboarding and instructions
- Looks for keyboard shortcuts immediately
- Tries to bulk-select, batch-edit, and automate
- Gets frustrated by required steps that feel unnecessary
- Abandons if anything feels slow or patronizing
**Test Questions**:
- Can Alex complete the core task in under 60 seconds?
- Are there keyboard shortcuts for common actions?
- Can onboarding be skipped entirely?
- Do modals have keyboard dismiss (Esc)?
- Is there a "power user" path (shortcuts, bulk actions)?
**Red Flags** (report these specifically):
- Forced tutorials or unskippable onboarding
- No keyboard navigation for primary actions
- Slow animations that can't be skipped
- One-item-at-a-time workflows where batch would be natural
- Redundant confirmation steps for low-risk actions
---
## 2. Confused First-Timer: "Jordan"
**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
**Behaviors**:
- Reads all instructions carefully
- Hesitates before clicking anything unfamiliar
- Looks for help or support constantly
- Misunderstands jargon and abbreviations
- Takes the most literal interpretation of any label
**Test Questions**:
- Is the first action obviously clear within 5 seconds?
- Are all icons labeled with text?
- Is there contextual help at decision points?
- Does terminology assume prior knowledge?
- Is there a clear "back" or "undo" at every step?
**Red Flags** (report these specifically):
- Icon-only navigation with no labels
- Technical jargon without explanation
- No visible help option or guidance
- Ambiguous next steps after completing an action
- No confirmation that an action succeeded
---
## 3. Accessibility-Dependent User: "Sam"
**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
**Behaviors**:
- Tabs through the interface linearly
- Relies on ARIA labels and heading structure
- Cannot see hover states or visual-only indicators
- Needs adequate color contrast (4.5:1 minimum)
- May use browser zoom up to 200%
**Test Questions**:
- Can the entire primary flow be completed keyboard-only?
- Are all interactive elements focusable with visible focus indicators?
- Do images have meaningful alt text?
- Is color contrast WCAG AA compliant (4.5:1 for text)?
- Does the screen reader announce state changes (loading, success, errors)?
**Red Flags** (report these specifically):
- Click-only interactions with no keyboard alternative
- Missing or invisible focus indicators
- Meaning conveyed by color alone (red = error, green = success)
- Unlabeled form fields or buttons
- Time-limited actions without extension option
- Custom components that break screen reader flow
---
## 4. Deliberate Stress Tester: "Riley"
**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
**Behaviors**:
- Tests edge cases intentionally (empty states, long strings, special characters)
- Submits forms with unexpected data (emoji, RTL text, very long values)
- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
- Looks for inconsistencies between what the UI promises and what actually happens
- Documents problems methodically
**Test Questions**:
- What happens at the edges (0 items, 1000 items, very long text)?
- Do error states recover gracefully or leave the UI in a broken state?
- What happens on refresh mid-workflow? Is state preserved?
- Are there features that appear to work but produce broken results?
- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
**Red Flags** (report these specifically):
- Features that appear to work but silently fail or produce wrong results
- Error handling that exposes technical details or leaves UI in a broken state
- Empty states that show nothing useful ("No results" with no guidance)
- Workflows that lose user data on refresh or navigation
- Inconsistent behavior between similar interactions in different parts of the UI
---
## 5. Distracted Mobile User: "Casey"
**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
**Behaviors**:
- Uses thumb only; prefers bottom-of-screen actions
- Gets interrupted mid-flow and returns later
- Switches between apps frequently
- Has limited attention span and low patience
- Types as little as possible, prefers taps and selections
**Test Questions**:
- Are primary actions in the thumb zone (bottom half of screen)?
- Is state preserved if the user leaves and returns?
- Does it work on slow connections (3G)?
- Can forms use autocomplete and smart defaults?
- Are touch targets at least 44×44pt?
**Red Flags** (report these specifically):
- Important actions positioned at the top of the screen (unreachable by thumb)
- No state persistence; progress lost on tab switch or interruption
- Large text inputs required where selection would work
- Heavy assets loading on every page (no lazy loading)
- Tiny tap targets or targets too close together
---
## Selecting Personas
Choose personas based on the interface type:
| Interface Type | Primary Personas | Why |
|---------------|-----------------|-----|
| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
| Dashboard / admin | Alex, Sam | Power users, accessibility |
| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
| Onboarding flow | Jordan, Casey | Confusion, interruption |
| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
---
## Project-Specific Personas
If `{{config_file}}` contains a `## Design Context` section (generated by `impeccable teach`), derive 12 additional personas from the audience and brand information:
1. Read the target audience description
2. Identify the primary user archetype not covered by the 5 predefined personas
3. Create a persona following this template:
```
### [Role]: "[Name]"
**Profile**: [2-3 key characteristics derived from Design Context]
**Behaviors**: [3-4 specific behaviors based on the described audience]
**Red Flags**: [3-4 things that would alienate this specific user type]
```
Only generate project-specific personas when real Design Context data is available. Don't invent audience details; use the 5 predefined personas when no context exists.
-1
View File
@@ -91,7 +91,6 @@ Visual polish on a misshapen flow is wasted work. Match the *shape* of the exper
- **Theme consistency**: Works in all theme variants
- **Color meaning**: Same colors mean same things throughout
- **Accessible focus**: Focus indicators visible with sufficient contrast
- **Tinted neutrals**: No pure gray or pure black; add subtle color tint (0.01 chroma)
- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency
### Interaction States
+22 -24
View File
@@ -10,47 +10,45 @@ Product UI's failure mode isn't flatness, it's strangeness without purpose: over
## Typography
- **System fonts are legitimate.** `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif` gives you native feel on every platform. Inter is the common cross-platform default for a reason.
- **One family is often right.** Product UIs don't need display/body pairing. A well-tuned sans carries headings, buttons, labels, body, data.
- **Fixed rem scale, not fluid.** Clamp-sized headings don't serve product UI. Users view at consistent DPI, and a fluid h1 that shrinks in a sidebar looks worse, not better.
- **Tighter scale ratio.** 1.1251.2 between steps is typical. More type elements here than on brand surfaces; exaggerated contrast creates noise.
- **Line length still applies for prose** (6575ch). Data and compact UI can run denser; tables at 120ch+ are fine.
- **One family is often right.** Product UIs don't need display/body pairing. A well-tuned sans carries headings, buttons, labels, body, data. <!-- rule:product-typo-one-family -->
- **Fixed rem scale, not fluid.** Clamp-sized headings don't serve product UI. Users view at consistent DPI, and a fluid h1 that shrinks in a sidebar looks worse, not better. <!-- rule:product-typo-fixed-rem-scale -->
- **Tighter scale ratio.** 1.1251.2 between steps is typical. More type elements here than on brand surfaces; exaggerated contrast creates noise. <!-- rule:product-typo-tighter-ratio -->
- **Line length still applies for prose** (6575ch). Data and compact UI can run denser; tables at 120ch+ are fine. <!-- rule:product-typo-line-length -->
## Color
Product defaults to Restrained. A single surface can earn Committed (a dashboard where one category color carries a report, an onboarding flow with a drenched welcome screen), but Restrained is the floor.
Product defaults to Restrained. A single surface can earn Committed (a dashboard where one category color carries a report, an onboarding flow with a drenched welcome screen), but Restrained is the floor. <!-- rule:product-color-restrained-default -->
- State-rich semantic vocabulary: hover, focus, active, disabled, selected, loading, error, warning, success, info. Standardize these.
- Accent color used for primary actions, current selection, and state indicators only, not decoration.
- A second neutral layer for sidebars, toolbars, and panels (slightly cooler or warmer than the content surface).
- State-rich semantic vocabulary: hover, focus, active, disabled, selected, loading, error, warning, success, info. Standardize these. <!-- rule:product-color-state-vocab -->
- Accent color used for primary actions, current selection, and state indicators only, not decoration. <!-- rule:product-color-accent-only -->
- A second neutral layer for sidebars, toolbars, and panels (slightly cooler or warmer than the content surface). <!-- rule:product-color-second-neutral -->
## Layout
- Predictable grids. Consistency IS an affordance; users navigate faster when the structure is expected.
- Familiar patterns are features. Standard navigation (top bar, side nav), breadcrumbs, tabs, and form layouts have established user expectations. Don't reinvent for flavor.
- Responsive behavior is structural (collapse sidebar, responsive table, breakpoint-driven columns), not fluid typography.
- Responsive behavior is structural (collapse sidebar, responsive table, breakpoint-driven columns), not fluid typography. <!-- rule:product-layout-responsive-structural -->
## Components
Every interactive component has: default, hover, focus, active, disabled, loading, error. Don't ship with half of these.
Every interactive component has: default, hover, focus, active, disabled, loading, error. Don't ship with half of these. <!-- rule:product-components-all-states -->
- Skeleton states for loading, not spinners in the middle of content.
- Empty states that teach the interface, not "nothing here."
- Consistent affordances across the surface. Same button shape. Same form-control vocabulary. Same icon style.
- Skeleton states for loading, not spinners in the middle of content. <!-- rule:product-components-skeleton-loading -->
- Empty states that teach the interface, not "nothing here." <!-- rule:product-components-empty-states -->
- Consistent affordances across the surface. Same button shape. Same form-control vocabulary. Same icon style. <!-- rule:product-components-consistent-affordances -->
## Motion
- 150250 ms on most transitions. Users are in flow; don't make them wait for choreography.
- Motion conveys state, not decoration. State change, feedback, loading, reveal: nothing else.
- No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load.
- 150250 ms on most transitions. Users are in flow; don't make them wait for choreography. <!-- rule:product-motion-quick-transitions -->
- Motion conveys state, not decoration. State change, feedback, loading, reveal: nothing else. <!-- rule:product-motion-state-not-decoration -->
- No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. <!-- rule:product-motion-no-page-load-sequence -->
## Product bans (on top of the shared absolute bans)
- Decorative motion that doesn't convey state.
- Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong.
- Display fonts in UI labels, buttons, data.
- Reinventing standard affordances for flavor (custom scrollbars, weird form controls, non-standard modals).
- Heavy color or full-saturation accents on inactive states.
- Decorative motion that doesn't convey state. <!-- rule:product-ban-decorative-motion -->
- Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. <!-- rule:product-ban-inconsistent-components -->
- Display fonts in UI labels, buttons, data. <!-- rule:product-ban-display-fonts-ui -->
- Reinventing standard affordances for flavor (custom scrollbars, weird form controls, non-standard modals). <!-- rule:product-ban-reinvented-affordances -->
- Heavy color or full-saturation accents on inactive states. <!-- rule:product-ban-heavy-inactive-color -->
- Modal as first thought. Modals are usually laziness. Exhaust inline / progressive alternatives first. <!-- rule:product-ban-modal-first-thought -->
## Product permissions
-114
View File
@@ -1,114 +0,0 @@
# Responsive Design
## Mobile-First: Write It Right
Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
## Breakpoints: Content-Driven
Don't chase device sizes; let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
## Detect Input Method, Not Just Screen Size
**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard. Use pointer and hover queries:
```css
/* Fine pointer (mouse, trackpad) */
@media (pointer: fine) {
.button { padding: 8px 16px; }
}
/* Coarse pointer (touch, stylus) */
@media (pointer: coarse) {
.button { padding: 12px 20px; } /* Larger touch target */
}
/* Device supports hover */
@media (hover: hover) {
.card:hover { transform: translateY(-2px); }
}
/* Device doesn't support hover (touch) */
@media (hover: none) {
.card { /* No hover state - use active instead */ }
}
```
**Critical**: Don't rely on hover for functionality. Touch users can't hover.
## Safe Areas: Handle the Notch
Modern phones have notches, rounded corners, and home indicators. Use `env()`:
```css
body {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
/* With fallback */
.footer {
padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
```
**Enable viewport-fit** in your meta tag:
```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
```
## Responsive Images: Get It Right
### srcset with Width Descriptors
```html
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w
"
sizes="(max-width: 768px) 100vw, 50vw"
alt="Hero image"
>
```
**How it works**:
- `srcset` lists available images with their actual widths (`w` descriptors)
- `sizes` tells the browser how wide the image will display
- Browser picks the best file based on viewport width AND device pixel ratio
### Picture Element for Art Direction
When you need different crops/compositions (not just resolutions):
```html
<picture>
<source media="(min-width: 768px)" srcset="wide.jpg">
<source media="(max-width: 767px)" srcset="tall.jpg">
<img src="fallback.jpg" alt="...">
</picture>
```
## Layout Adaptation Patterns
**Navigation**: Three stages: hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
## Testing: Don't Trust DevTools Alone
DevTools device emulation is useful for layout but misses:
- Actual touch interactions
- Real CPU/memory constraints
- Network latency patterns
- Font rendering differences
- Browser chrome/keyboard appearances
**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
---
**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.
+1 -1
View File
@@ -151,7 +151,7 @@ How users interact with this feature. What happens on click, hover, scroll? What
What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges. For image-led surfaces, also list the required image/media roles and their likely source (project asset, generated raster, semantic SVG/CSS, canvas/WebGL, icon library, or accepted omission).
**9. Recommended References**
Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., layout.md for complex layouts, animate.md for animated features, interaction-design.md for form-heavy features, typeset.md for typography-driven pages, colorize.md for color-led brands).
**10. Open Questions**
Anything genuinely unresolved. Don't list "open questions" you've already recommended a default for; assert the default and move on. If you'd write `Recommend: X` next to a question, just decide X.
-100
View File
@@ -1,100 +0,0 @@
# Spatial Design
## Spacing Systems
### Use 4pt Base, Not 8pt
8pt systems are too coarse; you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
### Name Tokens Semantically
Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing; it eliminates margin collapse and cleanup hacks.
## Grid Systems
### The Self-Adjusting Grid
Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
## Visual Hierarchy
### The Squint Test
Blur your eyes (or screenshot and blur). Can you still identify:
- The most important element?
- The second most important?
- Clear groupings?
If everything looks the same weight blurred, you have a hierarchy problem.
### Hierarchy Through Multiple Dimensions
Don't rely on size alone. Combine:
| Tool | Strong Hierarchy | Weak Hierarchy |
|------|------------------|----------------|
| **Size** | 3:1 ratio or more | <2:1 ratio |
| **Weight** | Bold vs Regular | Medium vs Regular |
| **Color** | High contrast | Similar tones |
| **Position** | Top/left (primary) | Bottom/right |
| **Space** | Surrounded by white space | Crowded |
**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
### Cards Are Not Required
Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards.** Use spacing, typography, and subtle dividers for hierarchy within a card.
## Container Queries
Viewport queries are for page layouts. **Container queries are for components**:
```css
.card-container {
container-type: inline-size;
}
.card {
display: grid;
gap: var(--space-md);
}
/* Card layout changes based on its container, not viewport */
@container (min-width: 400px) {
.card {
grid-template-columns: 120px 1fr;
}
}
```
**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands automatically, without viewport hacks.
## Optical Adjustments
Text at `margin-left: 0` looks indented due to letterform whitespace; use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
### Touch Targets vs Visual Size
Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
```css
.icon-button {
width: 24px; /* Visual size */
height: 24px;
position: relative;
}
.icon-button::before {
content: '';
position: absolute;
inset: -10px; /* Expand tap target to 44px */
}
```
## Depth & Elevation
Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle. If you can clearly see it, it's probably too strong.
---
**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.
+157 -2
View File
@@ -4,7 +4,7 @@ Typography carries most of the information on the page. Replace generic defaults
## Register
Brand: run the font selection procedure in [brand.md](brand.md). Pairing follows the brand's lane (display serif + sans body for editorial/luxury, one committed sans for tech, etc.). Fluid `clamp()` scale, ≥1.25 ratio between steps.
Brand: run the font selection procedure in [brand.md](brand.md). Fluid `clamp()` scale, ≥1.25 ratio between steps.
Product: system fonts and familiar sans stacks are legitimate here. One well-tuned family typically carries the whole UI. Fixed `rem` scale, 1.1251.2 ratio between more closely-spaced steps.
@@ -43,7 +43,7 @@ Analyze what's weak or generic about the current type:
## Plan Typography Improvements
Consult the [typography reference](typography.md) for detailed guidance on scales, pairing, and loading strategies.
Consult the [Reference Material](#reference-material) section below for detailed guidance on scales, pairing, and loading strategies.
Create a systematic plan:
@@ -122,3 +122,158 @@ Each variant MUST declare a `scale` param controlling the hierarchy ratio. Expre
Where the variant riffs on a specific pairing, expose the pairing choice as a `steps` param (e.g. "serif display + sans body" vs. "mono display + sans body" vs. "all-sans"). Each branch routes through `:scope[data-p-pairing="X"]` selectors in scoped CSS.
See `reference/live.md` for the full params contract.
---
## Reference Material
The sections below were previously `typography.md` and live inline now so the typeset flow has its deep typography reference in one place. `bolder.md` also references this section.
### Typography
#### Classic Typography Principles
##### Vertical Rhythm
Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony; text and space share a mathematical foundation.
##### Modular Scale & Hierarchy
The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
**Use fewer sizes with more contrast.** A 5-size system covers most needs:
| Role | Typical Ratio | Use Case |
|------|---------------|----------|
| xs | 0.75rem | Captions, legal |
| sm | 0.875rem | Secondary UI, metadata |
| base | 1rem | Body text |
| lg | 1.25-1.5rem | Subheadings, lead text |
| xl+ | 2-4rem | Headlines, hero text |
Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
##### Readability & Measure
Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length: narrow columns need tighter leading, wide columns need more.
**Non-obvious**: Light text on dark backgrounds needs compensation on three axes, not just one. Bump line-height by 0.050.1, add a touch of letter-spacing (0.010.02em), and optionally step the body weight up one notch (regular → medium). The perceived weight drops across all three; fix all three.
**Paragraph rhythm**: Pick either space between paragraphs OR first-line indentation. Never both. Digital usually wants space; editorial/long-form can justify indent-only.
#### Font Selection & Pairing
The tactical selection procedure and the reflex-reject list live in [reference/brand.md](brand.md) under **Font selection procedure** and **Reflex-reject list** (loaded for brand-register tasks). The rest of this section covers the adjacent knowledge: anti-reflex corrections, system font use, and pairing rules.
##### Anti-reflexes worth defending against
- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
- A children's product does NOT need a rounded display font. Kids' books use real type.
- A "modern" brief does NOT need a geometric sans. The most modern thing you can do is not use the font everyone else is using.
**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
##### Pairing Principles
**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
When pairing, contrast on multiple axes:
- Serif + Sans (structure contrast)
- Geometric + Humanist (personality contrast)
- Condensed display + Wide body (proportion contrast)
##### Web Font Loading
The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
```css
/* 1. Use font-display: swap for visibility */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap;
}
/* 2. Match fallback metrics to minimize shift */
@font-face {
font-family: 'CustomFont-Fallback';
src: local('Arial');
size-adjust: 105%; /* Scale to match x-height */
ascent-override: 90%; /* Match ascender height */
descent-override: 20%; /* Match descender depth */
line-gap-override: 10%; /* Match line spacing */
}
body {
font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
}
```
Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
**`swap` vs `optional`**: `swap` shows fallback text immediately and FOUT-swaps when the web font arrives. `optional` uses the fallback if the web font misses a small load budget (~100ms) and avoids the shift entirely. Pick `optional` when zero layout shift matters more than seeing the branded font on slow networks.
**Preload the critical weight only**: typically the regular-weight body font used above the fold. Preloading every weight costs more bandwidth than it saves.
**Variable fonts for 3+ weights or styles**: a single variable font file is usually smaller than three static weight files, gives fractional weight control, and pairs well with `font-optical-sizing: auto`. For 12 weights, static is fine.
#### Modern Web Typography
##### Fluid Type
Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate (higher vw = faster scaling). Add a rem offset so it doesn't collapse to 0 on small screens.
**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI; fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
**Bound your clamp()**: keep `max-size ≤ ~2.5 × min-size`. Wider ratios break the browser's zoom and reflow behaviour and make large viewports feel like the page is shouting.
**Scale container width and font-size together** so effective character measure stays in the 4575ch band at every viewport. A heading that widens faster than its container drifts out of the comfortable measure at the top end.
##### OpenType Features
Most developers don't know these exist. Use them for polish:
```css
/* Proper fractions */
.recipe-amount { font-variant-numeric: diagonal-fractions; }
/* Small caps for abbreviations */
abbr { font-variant-caps: all-small-caps; }
/* Disable ligatures in code */
code { font-variant-ligatures: none; }
/* Enable kerning (usually on by default, but be explicit) */
body { font-kerning: normal; }
```
Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
##### Rendering polish
```css
/* Variable fonts: pick the right optical-size master automatically */
body { font-optical-sizing: auto; }
```
**ALL-CAPS tracking**: capitals sit too close at default spacing. Add 512% letter-spacing (`letter-spacing: 0.05em` to `0.12em`) to short all-caps labels, eyebrows, and small headings. Real small caps (via `font-variant-caps`) need the same treatment, slightly gentler.
#### Typography System Architecture
Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
#### Accessibility Considerations
Beyond contrast ratios (which are well-documented), consider:
- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
---
**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.
-159
View File
@@ -1,159 +0,0 @@
# Typography
## Classic Typography Principles
### Vertical Rhythm
Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony; text and space share a mathematical foundation.
### Modular Scale & Hierarchy
The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
**Use fewer sizes with more contrast.** A 5-size system covers most needs:
| Role | Typical Ratio | Use Case |
|------|---------------|----------|
| xs | 0.75rem | Captions, legal |
| sm | 0.875rem | Secondary UI, metadata |
| base | 1rem | Body text |
| lg | 1.25-1.5rem | Subheadings, lead text |
| xl+ | 2-4rem | Headlines, hero text |
Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
### Readability & Measure
Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length: narrow columns need tighter leading, wide columns need more.
**Non-obvious**: Light text on dark backgrounds needs compensation on three axes, not just one. Bump line-height by 0.050.1, add a touch of letter-spacing (0.010.02em), and optionally step the body weight up one notch (regular → medium). The perceived weight drops across all three; fix all three.
**Paragraph rhythm**: Pick either space between paragraphs OR first-line indentation. Never both. Digital usually wants space; editorial/long-form can justify indent-only.
## Font Selection & Pairing
The tactical selection procedure and the reflex-reject list live in [reference/brand.md](brand.md) under **Font selection procedure** and **Reflex-reject list** (loaded for brand-register tasks). The rest of this section covers the adjacent knowledge: anti-reflex corrections, system font use, and pairing rules.
### Anti-reflexes worth defending against
- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
- A children's product does NOT need a rounded display font. Kids' books use real type.
- A "modern" brief does NOT need a geometric sans. The most modern thing you can do is not use the font everyone else is using.
**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
### Pairing Principles
**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
When pairing, contrast on multiple axes:
- Serif + Sans (structure contrast)
- Geometric + Humanist (personality contrast)
- Condensed display + Wide body (proportion contrast)
**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
### Web Font Loading
The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
```css
/* 1. Use font-display: swap for visibility */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap;
}
/* 2. Match fallback metrics to minimize shift */
@font-face {
font-family: 'CustomFont-Fallback';
src: local('Arial');
size-adjust: 105%; /* Scale to match x-height */
ascent-override: 90%; /* Match ascender height */
descent-override: 20%; /* Match descender depth */
line-gap-override: 10%; /* Match line spacing */
}
body {
font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
}
```
Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
**`swap` vs `optional`**: `swap` shows fallback text immediately and FOUT-swaps when the web font arrives. `optional` uses the fallback if the web font misses a small load budget (~100ms) and avoids the shift entirely. Pick `optional` when zero layout shift matters more than seeing the branded font on slow networks.
**Preload the critical weight only**: typically the regular-weight body font used above the fold. Preloading every weight costs more bandwidth than it saves.
**Variable fonts for 3+ weights or styles**: a single variable font file is usually smaller than three static weight files, gives fractional weight control, and pairs well with `font-optical-sizing: auto`. For 12 weights, static is fine.
## Modern Web Typography
### Fluid Type
Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate (higher vw = faster scaling). Add a rem offset so it doesn't collapse to 0 on small screens.
**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI; fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
**Bound your clamp()**: keep `max-size ≤ ~2.5 × min-size`. Wider ratios break the browser's zoom and reflow behaviour and make large viewports feel like the page is shouting.
**Scale container width and font-size together** so effective character measure stays in the 4575ch band at every viewport. A heading that widens faster than its container drifts out of the comfortable measure at the top end.
### OpenType Features
Most developers don't know these exist. Use them for polish:
```css
/* Tabular numbers for data alignment */
.data-table { font-variant-numeric: tabular-nums; }
/* Proper fractions */
.recipe-amount { font-variant-numeric: diagonal-fractions; }
/* Small caps for abbreviations */
abbr { font-variant-caps: all-small-caps; }
/* Disable ligatures in code */
code { font-variant-ligatures: none; }
/* Enable kerning (usually on by default, but be explicit) */
body { font-kerning: normal; }
```
Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
### Rendering polish
```css
/* Even out heading line lengths (browser picks better break points) */
h1, h2, h3 { text-wrap: balance; }
/* Reduce orphans and ragged endings in long prose */
article p { text-wrap: pretty; }
/* Variable fonts: pick the right optical-size master automatically */
body { font-optical-sizing: auto; }
```
**ALL-CAPS tracking**: capitals sit too close at default spacing. Add 512% letter-spacing (`letter-spacing: 0.05em` to `0.12em`) to short all-caps labels, eyebrows, and small headings. Real small caps (via `font-variant-caps`) need the same treatment, slightly gentler.
## Typography System Architecture
Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
## Accessibility Considerations
Beyond contrast ratios (which are well-documented), consider:
- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
---
**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.
-107
View File
@@ -1,107 +0,0 @@
# UX Writing
## The Button Label Problem
**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
| Bad | Good | Why |
|-----|------|-----|
| OK | Save changes | Says what will happen |
| Submit | Create account | Outcome-focused |
| Yes | Delete message | Confirms the action |
| Cancel | Keep editing | Clarifies what "cancel" means |
| Click here | Download PDF | Describes the destination |
**For destructive actions**, name the destruction:
- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
- "Delete 5 items" not "Delete selected" (show the count)
## Error Messages: The Formula
Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
### Error Message Templates
| Situation | Template |
|-----------|----------|
| **Format error** | "[Field] needs to be [format]. Example: [example]" |
| **Missing required** | "Please enter [what's missing]" |
| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
### Don't Blame the User
Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
## Empty States Are Opportunities
Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
## Voice vs Tone
**Voice** is your brand's personality, consistent everywhere.
**Tone** adapts to the moment.
| Moment | Tone Shift |
|--------|------------|
| Success | Celebratory, brief: "Done! Your changes are live." |
| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
| Loading | Reassuring: "Saving your work..." |
| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
## Writing for Accessibility
**Link text** must have standalone meaning: "View pricing plans" not "Click here". **Alt text** describes information, not the image: "Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
## Writing for Translation
### Plan for Expansion
German text is ~30% longer than English. Allocate space:
| Language | Expansion |
|----------|-----------|
| German | +30% |
| French | +20% |
| Finnish | +30-40% |
| Chinese | -30% (fewer chars, but same width) |
### Translation-Friendly Patterns
Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
## Consistency: The Terminology Problem
Pick one term and stick with it:
| Inconsistent | Consistent |
|--------------|------------|
| Delete / Remove / Trash | Delete |
| Settings / Preferences / Options | Settings |
| Sign in / Log in / Enter | Sign in |
| Create / Add / New | Create |
Build a terminology glossary and enforce it. Variety creates confusion.
## Avoid Redundant Copy
If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
## Loading States
Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
## Confirmation Dialogs: Use Sparingly
Most confirmation dialogs are design failures; consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
## Form Instructions
Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
---
**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.