mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-22 02:56:52 +03:00
Add concept world catalog and review workflow
AI-assisted: prepared by Codex at Paul's request.
This commit is contained in:
@@ -52,11 +52,11 @@ Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the
|
||||
|
||||
**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
|
||||
|
||||
### 5. Anti-Patterns (CRITICAL)
|
||||
### 5. Implementation Integrity (CRITICAL)
|
||||
|
||||
Check against ALL the **DON'T** guidelines from the parent impeccable skill (already loaded in this context). Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
|
||||
Run the bundled detector and verify each finding in context. Look for repeated implementation shortcuts, design-system drift, misleading or decorative content, and structure that is interchangeable with an unrelated product. Keep deterministic findings separate from visual judgment and call out false positives.
|
||||
|
||||
**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
|
||||
**Score 0-4**: 0=systemic drift, 1=major repeated failures, 2=several verified issues, 3=minor isolated issues, 4=coherent and intentional
|
||||
|
||||
## Generate Report
|
||||
|
||||
@@ -68,13 +68,13 @@ Check against ALL the **DON'T** guidelines from the parent impeccable skill (alr
|
||||
| 2 | Performance | ? | |
|
||||
| 3 | Responsive Design | ? | |
|
||||
| 4 | Theming | ? | |
|
||||
| 5 | Anti-Patterns | ? | |
|
||||
| 5 | Implementation Integrity | ? | |
|
||||
| **Total** | | **??/20** | **[Rating band]** |
|
||||
|
||||
**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
|
||||
|
||||
### Anti-Patterns Verdict
|
||||
**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
|
||||
### Implementation Integrity Verdict
|
||||
**Start here.** Pass/fail: does the implementation express a coherent product-specific system? Cite verified evidence and detector findings.
|
||||
|
||||
### Executive Summary
|
||||
- Audit Health Score: **??/20** ([rating band])
|
||||
@@ -93,7 +93,7 @@ Tag every issue with **P0-P3 severity**:
|
||||
For each issue, document:
|
||||
- **[P?] Issue name**
|
||||
- **Location**: Component, file, line
|
||||
- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
|
||||
- **Category**: Accessibility / Performance / Theming / Responsive / Implementation Integrity
|
||||
- **Impact**: How it affects users
|
||||
- **WCAG/Standard**: Which standard it violates (if applicable)
|
||||
- **Recommendation**: How to fix it
|
||||
@@ -132,4 +132,3 @@ After presenting the summary, tell the user:
|
||||
- Skip positive findings (celebrate what works)
|
||||
- Forget to prioritize (everything can't be P0)
|
||||
- Report false positives without verification
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the
|
||||
- **Hard-coded colors**: raw hex instead of semantic system colors (iOS) / Material color roles (Android) / design tokens
|
||||
- **Broken dark appearance**: missing dark variants, poor contrast in dark, quick inverts
|
||||
- **Dynamic Color** (Android 12+): no static fallback scheme, or ignored where it fits
|
||||
- **Off-platform materials**: hand-rolled blur/glassmorphism instead of system materials or tonal elevation
|
||||
- **Off-platform materials**: hand-rolled visual materials where system materials or tonal elevation are expected
|
||||
|
||||
**Score 0-4**: 0=Hard-coded everything, 1=Minimal tokens, 2=Partial (tokens exist, inconsistently used), 3=Good (minor hard-coded values), 4=Excellent (semantic throughout, both appearances first-class)
|
||||
|
||||
@@ -48,7 +48,7 @@ Score against the loaded platform reference(s), including their slop tests. **Ch
|
||||
- **Off-platform navigation**: custom global nav, overloaded tab bars, iOS patterns on Android or vice versa
|
||||
- **Web-shaped controls**: HTML-style buttons, custom toggles, hover-dependent affordances
|
||||
- **Icon drift**: mixed icon sets instead of SF Symbols / Material Symbols
|
||||
- **AI tells**: the shared absolute bans still apply (AI palette, gradient text, hero metrics)
|
||||
- **System drift**: repeated shortcuts or decorative patterns that conflict with the product, platform, or established design system
|
||||
|
||||
**Score 0-4**: 0=Web port (nothing native), 1=Heavy violations (3-4 kinds), 2=Some (1-2 noticeable), 3=Mostly conformant (subtle issues), 4=Fully native (a fluent user trusts every screen)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ Use the project's existing color space. For a new web palette, prefer OKLCH beca
|
||||
- In dark mode, design surface elevation and contrast explicitly; do not invert the light theme mechanically.
|
||||
- Define primitive values and semantic tokens when the project has a token system. Theme changes should normally remap semantic roles.
|
||||
|
||||
Avoid decorative color that has no relation to hierarchy, state, content, or the visual world. Generic gradients, blobs, side stripes, and arbitrary colored headings are not a color strategy.
|
||||
Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy.
|
||||
|
||||
## Contrast and perception
|
||||
|
||||
|
||||
@@ -54,13 +54,13 @@ If browser automation is available, each assessment creates its own new tab. Nev
|
||||
Read relevant source files and visually inspect the live page when browser automation is available. Think like a design director.
|
||||
|
||||
Evaluate:
|
||||
- **AI slop**: Would someone believe "AI made this" immediately? Check all DON'T guidance from the parent Impeccable skill.
|
||||
- **Design specificity**: Is the composition, interaction, and visual language grounded in this product, or could an unrelated product use it unchanged? Make this judgment before seeing detector output.
|
||||
- **Holistic design**: hierarchy, IA, emotional fit, discoverability, composition, typography, color, accessibility, states, copy, and edge cases.
|
||||
- **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 the [Heuristics Scoring Guide](#heuristics-scoring-guide) section below; score all 10 heuristics 0-4, marking any heuristic the mode-applicability rule allows as `n/a` instead of forcing a number.
|
||||
|
||||
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.
|
||||
Return: design-specificity verdict, heuristic scores, cognitive load, emotional journey, 2-3 strengths, 3-5 priority issues, persona red flags, minor observations, and provocative questions.
|
||||
|
||||
### Assessment B: Detector + Browser Evidence
|
||||
|
||||
@@ -138,11 +138,11 @@ Be honest with scores. A 4 means genuinely excellent. Most real interfaces score
|
||||
|
||||
**Mode applicability**: heuristics 7 (Flexibility and Efficiency) and 10 (Help and Documentation) may be scored `n/a` on Persuade and Experience surfaces (landing pages, campaigns, portfolios, bodies of work), as may any other heuristic that genuinely cannot apply to the surface under review. Write `n/a` in the Score cell with a one-line reason, and renormalize the total to the applicable maximum (e.g. **24/32** when two heuristics are n/a) so the rating band stays proportional. The persisted snapshot must record which heuristics were scored n/a.
|
||||
|
||||
#### Anti-Patterns Verdict
|
||||
#### Design Specificity Verdict
|
||||
|
||||
**Start here.** Does this look AI-generated?
|
||||
**Start here.** Does the result feel authored for this product, or category-interchangeable?
|
||||
|
||||
**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
|
||||
**LLM assessment**: Your unanchored evaluation of design specificity. Cover overall coherence, structural sameness, category-interchangeable choices, and missed opportunities for product character.
|
||||
|
||||
**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
|
||||
|
||||
@@ -206,7 +206,7 @@ Once the report above is finalized, write it to `.impeccable/critique/` so the u
|
||||
|
||||
Skip this step if the Setup slug was null (vague or root-level target).
|
||||
|
||||
1. **Write the body to a temp file** so you can pipe it to the helper. Use the full critique report (heuristic table, anti-patterns verdict, priority issues, persona red flags, minor observations, and questions), but stop before the "Ask the User" / "Recommended Actions" sections that come later.
|
||||
1. **Write the body to a temp file** so you can pipe it to the helper. Use the full critique report (heuristic table, design-specificity verdict, priority issues, persona red flags, minor observations, and questions), but stop before the "Ask the User" / "Recommended Actions" sections that come later.
|
||||
|
||||
<codex>
|
||||
Codex: exclude Run Notes from the temp body file; Run Notes are final-chat only because persistence, trend read, and temp cleanup happen after the snapshot write.
|
||||
|
||||
@@ -32,7 +32,7 @@ State in one sentence what the user should feel and why that feeling belongs to
|
||||
- an illustration, sound, haptic, or environmental detail grounded in the product world;
|
||||
- a discovery reward that reveals real utility.
|
||||
|
||||
Derive the treatment from product mechanism and visual world. Do not select from a stock catalog of confetti, particles, mascots, novelty cursors, jokes, or hover lifts.
|
||||
Derive the treatment from product mechanism and visual world, not a stock catalog.
|
||||
|
||||
## Build for the emotional moment
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ Concrete visual guardrails grounded in the incumbent implementation or the user'
|
||||
- **Do** [...]
|
||||
|
||||
### Don't:
|
||||
- **Don't** [specific prohibition, e.g. "use border-left greater than 1px as a colored stripe"].
|
||||
- **Don't** [specific prohibition confirmed by the incumbent system or the user].
|
||||
- **Don't** [...]
|
||||
- **Don't** [...]
|
||||
```
|
||||
@@ -355,7 +355,7 @@ For projects with no visual system to extract yet. Produces a user-chosen visual
|
||||
|
||||
PRODUCT.md is the prerequisite. If it is missing, load [init.md](init.md) and complete its product interview first. Do not create a visual identity without durable product context.
|
||||
|
||||
If PRODUCT.md exists, load [new-work.md](new-work.md), resolve visual authority, and run **Establish or replace the visual world** only when no authority exists or replacement was explicitly requested. Stop after its directional DESIGN.md seed; `document --seed` does not need a task concept. A structured simulated user counts as the user and must get the same choice.
|
||||
If PRODUCT.md exists, load [new-work.md](new-work.md) and resolve visual authority. Seed mode requires a concrete first surface: use the target the user named, or ask what they want to make first. Run **Select one direction** for A, D, or E so the visual world and its first expression are chosen as one pair. Stop after the directional DESIGN.md seed and surface brief; do not implement. A structured simulated user counts as the user and must get the same choice.
|
||||
|
||||
If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again.
|
||||
|
||||
@@ -371,7 +371,7 @@ Lead the file with:
|
||||
|
||||
Per-section guidance in seed mode:
|
||||
|
||||
- **Overview**: the chosen design thesis, layout behavior, material character, imagery stance, motion grammar, and reusable signature. Do not promote the current page's first-view idea into the global world.
|
||||
- **Overview**: the chosen design thesis, layout behavior, material character, imagery stance, motion grammar, and reusable signature. Keep the selected first-surface expression in its surface brief; do not promote its composition into the global world.
|
||||
- **Colors**: the selected palette strategy and roles. Include values only when the user, an existing asset, or new-work's exploration established them; otherwise mark them `[to be resolved during implementation]`.
|
||||
- **Typography**: the selected type character and role relationship. Include font names only when established; otherwise mark the pairing `[to be resolved during implementation]`.
|
||||
- **Layout**: the selected spatial grammar and responsive behavior, without pretending exact measurements are settled.
|
||||
@@ -399,7 +399,7 @@ Your own write is the freshest source; no reload needed.
|
||||
- **Exact values in parens**: hex codes, px/rem values, font weights; always the number in parens alongside the description.
|
||||
- **Use Named Rules**: `**The [Name] Rule.** [short doctrine]`. These are memorable, citable, and much stickier for AI consumers than bullet lists. Stitch's own outputs use them heavily ("The No-Line Rule", "The Ghost Border Fallback"). Aim for 1-3 per section.
|
||||
- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance.
|
||||
- **Concrete anti-pattern tests**. Stitch writes things like *"If it looks like a 2014 app, the shadow is too dark and the blur is too small."* A one-sentence audit test beats a paragraph of principle.
|
||||
- **Use concrete audit tests only when they are grounded in the observed system or a confirmed user decision.** A one-sentence test beats a paragraph of principle.
|
||||
- **Reference PRODUCT.md selectively.** Product truth explains why the world fits; it does not supply page composition or a visual don't-list by default.
|
||||
- **Group colors by role**, not by hex-order or hue-order. Primary / Secondary / Tertiary / Neutral is the spec ordering.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Manage the **design detector hook** for the current project.
|
||||
|
||||
The hook runs the impeccable design detector on direct file edits to design-relevant files (`.tsx`, `.jsx`, `.html`, `.vue`, `.svelte`, `.astro`, `.css`, `.scss`, `.sass`, `.less`, `.ts`, `.js`). Claude Code, Codex, and GitHub Copilot use a post-tool-use hook and push a short system reminder into the agent's context after the edit; findings get a correction prompt, pending issues get a re-nudge, and clean UI-ish files get a short ack unless quiet mode is on (`hook.quiet` in config). Plain `.ts` and `.js` files are still scanned, but stay quiet unless the detector finds something. Cursor uses `preToolUse` to block bad proposed writes before they land and stays silent when it allows a clean write.
|
||||
|
||||
The detector rules run in two tiers. The per-edit hook surfaces only the immediate tier: mechanical, unambiguous problems worth interrupting an edit for, such as broken images, overflowing or clipped content, contrast and legibility failures, gradient text, glow shadows, and design-system drift. Everything else (copy cadence, palette and typography taste, layout rhythm) is deferred to a deep pass on the `Stop` hook event, which runs the full rule set over every UI file touched in the session and surfaces the remaining findings once, deduplicated against what the per-edit pass already reported. A session that touched no UI files stops silently. Set `hook.perEditRules` to `"all"` in `.impeccable/config.json` to restore the full rule set on every edit. The Stop deep pass is wired for Claude Code and Codex, which both dispatch a native `Stop` hook event. Cursor does not get one (its stop hook is not consistently dispatched; the pre-write gate covers it), and GitHub Copilot's stop-style events do not feed context back to the model, so Copilot keeps the full rule set per edit instead of deferring.
|
||||
The detector rules run in two tiers. The per-edit hook surfaces only the immediate tier: mechanical, unambiguous problems worth interrupting an edit for, such as broken images, overflowing or clipped content, contrast and legibility failures, gradient text, glow shadows, and design-system drift. Everything else (copy cadence, palette and typography taste, layout rhythm) is deferred to a deep pass on the `Stop` hook event, which runs the full rule set over every UI file touched in the session and surfaces the remaining findings once, deduplicated against what the per-edit pass already reported. That Stop message also asks for one authored review of detector-blind model reflexes. A session that touched no UI files stops silently. Set `hook.perEditRules` to `"all"` in `.impeccable/config.json` to restore the full rule set on every edit. The Stop deep pass is wired for Claude Code and Codex, which both dispatch a native `Stop` hook event. Cursor does not get one (its stop hook is not consistently dispatched; the pre-write gate covers it), and GitHub Copilot's stop-style events do not feed context back to the model, so they keep the full detector per edit while `context.mjs` supplies the detector-blind review. When no automatic hook is active, `context.mjs` instead supplies that review plus one manual detector command.
|
||||
|
||||
This command toggles the hook **per project** by editing `.impeccable/config.json` (the unified Impeccable config; hook runtime settings live under its `hook` key, and shared detector ignores live under `detector`). Per-developer overrides, including the install consent decision (`hook.consent`) the CLI records, live in the gitignored `.impeccable/config.local.json`. Set `hook.enabled: false` to turn the hook off, `hook.quiet: true` to silence the clean/pending acks, or `hook.auditLog` to a file path for an NDJSON log. The legacy `IMPECCABLE_HOOK_DISABLED`, `IMPECCABLE_HOOK_QUIET`, and `IMPECCABLE_HOOK_LOG` env vars are still honored and override these config values when set.
|
||||
|
||||
|
||||
@@ -92,6 +92,10 @@ web
|
||||
|
||||
Platform is the bare value `web`, `ios`, `android`, or `adaptive`. Preserve useful legacy headings. New files go at `PROJECT_ROOT/PRODUCT.md`; otherwise update the resolved file. Write it before any visual-world or surface-concept work.
|
||||
|
||||
### Completion gate
|
||||
|
||||
Before loading new-work or resuming shape/build, verify that PRODUCT.md exists at the resolved path and contains the confirmed product record. If the file is absent, init is incomplete. Do not substitute interview notes, a planning packet, or later design prose for the file.
|
||||
|
||||
## Step 5: Configure live mode when useful
|
||||
|
||||
Skip native or non-runnable projects and leave existing config untouched. Otherwise follow [live.md](live.md)'s first-time setup. Any CSP source edit still requires its stated consent.
|
||||
|
||||
+51
-152
@@ -1,185 +1,84 @@
|
||||
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.
|
||||
Layout turns product priority into reading order, grouping, rhythm, and usable space. Diagnose the structural problem before moving boxes.
|
||||
|
||||
---
|
||||
|
||||
## Visitor mode
|
||||
|
||||
Persuade + Experience: asymmetric compositions, fluid spacing with `clamp()`, intentional grid-breaking for emphasis. Rhythm through contrast: tight groupings paired with generous separations.
|
||||
- **Persuade + Experience:** composition may be asymmetric, fluid, or intentionally disruptive when the selected world earns it.
|
||||
- **Operate + Read:** predictable structure, stable density, and navigable linearity are affordances.
|
||||
- **Native:** follow [ios.md](ios.md) or [android.md](android.md) for navigation, insets, adaptation, and touch targets.
|
||||
|
||||
Operate + Read: predictable grids, consistent densities, familiar navigation patterns. Responsive behavior is structural (collapse sidebar, responsive table), not fluid typography. Consistency IS an affordance. Read surfaces specifically want navigable linearity: a steady column the reader can follow and a structure they can hold in their head, not compositional surprise.
|
||||
Preserve the established visual world. A layout command changes structure inside it; identity replacement belongs to [new-work.md](new-work.md).
|
||||
|
||||
Native (`ios` / `android` / `adaptive`): structure follows the Layout section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): platform navigation, insets, and touch targets, never the CSS tooling below.
|
||||
## Two isolated assessments
|
||||
|
||||
---
|
||||
When a sub-agent tool is available and permitted, run these independently; otherwise run them yourself in this order.
|
||||
|
||||
## Two isolated assessments (required)
|
||||
|
||||
Spawn two parallel sub-agents whenever a sub-agent/Task tool is exposed: one for the layout assessment, one for the mechanical pre-scan. If the harness needs explicit user permission for sub-agents, stop and ask before proceeding. Isolation is the point: detector output anchors visual judgment toward what the scan can see, so neither sub-agent gets the other's output. Each assessment runs in its own sub-agent; running either one in this context when a sub-agent tool exists is not permitted, even when it is faster; the fallback below is only for sessions with no sub-agent tool. Give each a self-contained prompt (target files, visitor mode, documented spacing scale when present, and its instructions below); do not assume it can read this file.
|
||||
|
||||
**Sub-agent A (layout assessment)**: give it the full [Assess Current Layout](#assess-current-layout) checklist below, verbatim, in its prompt. It works through every item and returns per-item findings citing file, selector, or value.
|
||||
|
||||
**Sub-agent B (mechanical pre-scan)**: run the bundled detector scoped to layout:
|
||||
1. **Layout assessment:** inspect representative states and viewports. Answer every question below with rendered or source evidence:
|
||||
- **Reading order:** Apply the squint test. With detail blurred, can you still identify the primary element, the secondary element, and the major groups in order?
|
||||
- **Grouping:** Are related items close and distinct groups separated, or are containers compensating for weak proximity?
|
||||
- **Rhythm:** Do tight and generous intervals create a deliberate cadence, or is one spacing value repeated until everything has equal weight?
|
||||
- **Structure:** Does the topology match the content and task? Are repeated cards, columns, or sections genuinely equivalent, or merely a framework default?
|
||||
- **Density:** Does the amount of information per region fit use frequency, decision complexity, and visitor mode?
|
||||
- **Adaptation:** At narrow, intermediate, wide, zoomed, and localized states, what reorders, collapses, wraps, scrolls, or remains fixed? Does DOM and focus order still agree with the visual order?
|
||||
- **Extremes:** Do long content, empty states, overlays, sticky elements, safe areas, and small touch targets expose structural failures?
|
||||
2. **Mechanical scan:** run:
|
||||
|
||||
```bash
|
||||
node {{scripts_path}}/detect.mjs --json --scope layout [target files or dirs]
|
||||
```
|
||||
|
||||
A missing `node` on PATH is not permission to skip: hunt for a runtime (`command -v node`, nvm or Homebrew paths, the harness's own bundled node) and run it by full path. If none exists, halt the scan and report that Node must be installed (the parent relays this to the user); do **not** substitute grep for the detector or proceed unscanned. The detector abstains on arbitrary Tailwind spacing (`gap-[13px]`, `p-[7px]`) and ad-hoc `z-index` stacks, so when the project documents a spacing scale, also grep `gap-\[`, `p[trblxy]?-\[`, `m[trblxy]?-\[`, `z-\[` and judge those hits against it. Return the findings JSON plus the grep verdicts.
|
||||
Also inspect arbitrary spacing, overflow, stacking, and container behavior the detector cannot resolve. Keep mechanical evidence out of the first assessment, then synthesize both passes before editing. A clean scan cannot prove hierarchy or rhythm.
|
||||
|
||||
**If no sub-agent tool is exposed (or the user declined)**: run both yourself, assessment first, pre-scan second, so the deterministic findings can't anchor the visual judgment. Keep that order even when the scan feels quicker to start with.
|
||||
## Set the spatial thesis
|
||||
|
||||
**Synthesize** once both are done: merge into a single findings list, noting where they agree and what each caught alone. Fix every finding, or list it as a deliberate exception for the user to accept. A clean scan is a floor, not a verdict: a monotone grid with uniform spacing passes every detector rule, which is exactly what the assessment exists to catch. State in your final summary which path ran (parallel sub-agents or single-context fallback).
|
||||
Before editing, name:
|
||||
|
||||
---
|
||||
- the primary reading or task path;
|
||||
- what belongs together and what must separate;
|
||||
- which element leads and which supports;
|
||||
- the intended density and spacing rhythm;
|
||||
- how the structure changes across containers, viewports, input modes, and content extremes.
|
||||
|
||||
## Assess Current Layout
|
||||
Choose the simplest structural model that expresses those relationships. Use layout primitives according to the relationships they control, and name reusable spacing and container roles semantically.
|
||||
|
||||
This checklist is sub-agent A's brief (on the fallback path, work through it yourself before the pre-scan). Analyze what's weak about the current spatial design:
|
||||
## Apply
|
||||
|
||||
1. **Spacing**:
|
||||
- Is spacing consistent or arbitrary? (Random padding/margin values)
|
||||
- Is all spacing the same? (Equal padding everywhere = no rhythm)
|
||||
- Are related elements grouped tightly, with generous space between groups?
|
||||
- Group by meaning. Use proximity before adding containers or decoration.
|
||||
- Create rhythm through deliberate contrast between tight and generous intervals.
|
||||
- Use a documented spacing scale rather than one-off values. A 4-unit base usually provides the useful middle steps that an 8-only scale misses.
|
||||
- Let hierarchy follow product priority, not framework defaults.
|
||||
- Keep distinct content visually distinct without turning every group into an isolated component.
|
||||
- Make responsive behavior structural: reorder, collapse, reflow, or reveal based on what remains important.
|
||||
- Prefer container-aware components when the same component appears in different contexts.
|
||||
- Use `gap` for sibling rhythm when it expresses the relationship more directly than child margins.
|
||||
- Keep touch targets usable even when their visible marks are small.
|
||||
- Use depth only when it clarifies state or hierarchy.
|
||||
- Make optical corrections only after inspecting the rendered result.
|
||||
|
||||
2. **Visual hierarchy**:
|
||||
- Apply the squint test: blur your (metaphorical) eyes. Can you still identify the most important element, second most important, and clear groupings?
|
||||
- Is hierarchy achieved effectively? (Space and weight alone can be enough; is the current approach working?)
|
||||
- Does whitespace guide the eye to what matters?
|
||||
Variation is not a goal by itself. Repetition should support recognition; break it only when content or priority changes.
|
||||
|
||||
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)
|
||||
## Verify
|
||||
|
||||
4. **Rhythm & variety**:
|
||||
- Does the layout have visual rhythm? (Alternating tight/generous spacing)
|
||||
- Is every section structured the same way? (Monotonous repetition)
|
||||
- Are there intentional moments of surprise or emphasis?
|
||||
- The squint test still reveals the primary, secondary, and major groups in order.
|
||||
- The reading and task path remains clear at every supported size.
|
||||
- Related content groups naturally; unrelated content does not blur together.
|
||||
- Tight and generous spacing create intentional rhythm instead of monotonous repetition.
|
||||
- Density matches use frequency and content complexity.
|
||||
- Long text, empty states, localization, zoom, and dynamic content do not break the structure.
|
||||
- Keyboard, touch, and assistive-technology order agree with the visual order.
|
||||
- The final mechanical scan has no unexplained findings.
|
||||
|
||||
5. **Density**:
|
||||
- Is the layout too cramped? (Not enough breathing room)
|
||||
- Is the layout too sparse? (Excessive whitespace without purpose)
|
||||
- Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
|
||||
Answer each item with rendered or source evidence, then rerun the scan. Do not substitute a bare “yes” for verification.
|
||||
|
||||
**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material; use it with intention.
|
||||
|
||||
## Plan Layout Improvements
|
||||
|
||||
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.
|
||||
- **Hierarchy strategy**: How will space communicate importance?
|
||||
- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
|
||||
- **Rhythm**: Where should spacing be tight vs generous?
|
||||
|
||||
## Improve Layout Systematically
|
||||
|
||||
### 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
|
||||
|
||||
### Create Visual Rhythm
|
||||
|
||||
- **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**: 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.
|
||||
- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
|
||||
- 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
|
||||
|
||||
- Don't default to card grids for everything; spacing and alignment create visual grouping naturally
|
||||
- Use cards only when content is truly distinct and actionable. Never nest cards inside cards
|
||||
- Vary card sizes, span columns, or mix cards with non-card content to break repetition
|
||||
|
||||
### 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 2–3 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
|
||||
|
||||
- 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
|
||||
- Make all spacing equal (variety creates hierarchy)
|
||||
- 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)
|
||||
- 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.
|
||||
|
||||
## Verify Layout Improvements
|
||||
|
||||
- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
|
||||
- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
|
||||
- **Hierarchy**: Is the most important content obvious within 2 seconds?
|
||||
- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
|
||||
- **Consistency**: Is the spacing system applied uniformly?
|
||||
- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
|
||||
|
||||
Answer each item above by citing the file, selector, or value that satisfies it; never a bare yes. Then re-run the pre-scan and fix until the count of unresolved items and unaccepted findings is zero.
|
||||
|
||||
When the rhythm and hierarchy land, hand off to `{{command_prefix}}impeccable polish` for the final pass.
|
||||
When the structure holds, hand off to `{{command_prefix}}impeccable polish`.
|
||||
|
||||
## Live-mode signature params
|
||||
|
||||
Each variant MUST declare a `density` param. Drive all spacing tokens in the variant's scoped CSS through `calc(var(--p-density, 1) * <base>)`: paddings, gaps, column widths. Users slide from airy to packed and see layout re-breathe with no regeneration.
|
||||
Every variant declares a coarse `density` parameter and authors spacing against `var(--p-density, 1)`.
|
||||
|
||||
```json
|
||||
{"id":"density","kind":"range","min":0.6,"max":1.4,"step":0.05,"default":1,"label":"Density"}
|
||||
```
|
||||
|
||||
For variants whose topology genuinely changes (stacked vs. side-by-side, grid vs. bento), use a `steps` param whose scoped CSS branches via `:scope[data-p-structure="X"]`. One structure param + one density param is a powerful combo; resist adding a third.
|
||||
|
||||
```json
|
||||
{"id":"structure","kind":"steps","default":"grid","label":"Structure","options":[
|
||||
{"value":"stacked","label":"Stacked"},
|
||||
{"value":"grid","label":"Grid"},
|
||||
{"value":"bento","label":"Bento"}
|
||||
]}
|
||||
```
|
||||
|
||||
See `reference/live.md` for the full params contract.
|
||||
Add one structural parameter only when the topology genuinely branches. Follow [live.md](live.md)'s parameter contract.
|
||||
|
||||
@@ -220,7 +220,7 @@ Write down what you see in **one sentence**. The sentence describes the surface
|
||||
|
||||
Be specific. "Modern" is not a color, "elegant" is not a type pairing, "clean" is not a layout. If you can't extract a real value for an axis, skip it rather than fabricate. The point is to record what is, not to describe what you wish it were.
|
||||
|
||||
Do not include adjectives that name an aesthetic family ("editorial-leaning", "terminal-flavored", "brutalist"); those are conclusions, not data. They belong to Phase C lane selection in departure mode, not to identity description. Letting them sneak into Phase A is how the identity-lock collapses into a self-fulfilling prophecy.
|
||||
Do not name an aesthetic family in this sentence; that is a conclusion, not observed identity data. Letting conclusions into Phase A collapses the identity lock into a self-fulfilling prophecy.
|
||||
|
||||
This sentence is the **identity lock**. Every variant must be readable as the same brand if rendered side by side. Skipping this phase is the primary cause of off-brand variants. Absence of DESIGN.md is never an excuse; extract from CSS and computed styles instead.
|
||||
|
||||
@@ -247,13 +247,13 @@ Three variants → three DIFFERENT axes. The trio reads as *the same brand at th
|
||||
|
||||
**While planning each variant, also name its 2–3 parameter knobs** (per the §7 budget table). Parameters are part of the design, not a decoration added afterward. If the variant explores density, expose a density knob. If it explores color commitment, expose a color-amount range. Deciding "what's tunable" during planning produces better knobs than retrofitting them onto finished HTML.
|
||||
|
||||
**Departure mode.** Each variant anchors to a different **aesthetic direction**, derived from PRODUCT.md's audience world and voice plus the current DESIGN.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this product are not the same as the right three for another, and picking from a list is itself the training-data reflex (the model selects "Swiss-grid, Terminal, Industrial-signage" every time because those are the furthest-from-editorial items in any enumerated list).
|
||||
**Departure mode.** Each variant anchors to a different **aesthetic direction**, derived from PRODUCT.md's audience world and voice plus the current DESIGN.md. Do not pick from a fixed catalog; derive directions from this product.
|
||||
|
||||
Instead, work from the brand:
|
||||
|
||||
1. Read PRODUCT.md's Brand Personality words. What physical, spatial, or material experiences would embody those words if design were not involved? (A personality described as "specific, earned, unmistakable" evokes a hand-stamped letter, a numbered print, a watchmaker's loupe. A personality described as "restless, loud, unfiltered" evokes a concert poster, a spray-painted wall, a megaphone.)
|
||||
1. Read PRODUCT.md's Brand Personality words. Derive physical, spatial, or material experiences that embody them without starting from a design style.
|
||||
2. From those physical experiences, derive three visual directions that are genuinely different from each other AND from the current surface you're departing.
|
||||
3. Avoid the **reflex-reject lanes** in [brand.md](brand.md). Don't trade one monoculture for another. If you find yourself reaching for "Swiss-grid" or "Terminal" or "Industrial-signage" by reflex, you are pattern-matching a catalog in your training data, not reading the brand. Start over from the personality words.
|
||||
3. Reject any direction chosen by reflex rather than derived from the brand. Start over from the personality words when the rationale could fit a neighboring product.
|
||||
4. Each direction must be expressible in one concrete sentence that names a real-world referent ("a museum exhibition label system for a contemporary art gallery" not "clean and minimal"). If your sentence contains only adjectives, it's not concrete enough.
|
||||
5. **While planning each direction, also name its 2–3 parameter knobs** (per the §7 budget table). The same principle as default mode: decide "what's tunable" during planning, not after writing the HTML. A departure-mode hero with 0 parameters is not "bold creative vision," it's a missed opportunity for the user to fine-tune the direction they pick.
|
||||
|
||||
@@ -263,7 +263,7 @@ Instead, work from the brand:
|
||||
|
||||
**Departure mode squint.** Two passes, family before sentence:
|
||||
|
||||
1. **Family pass.** Label each variant with one design-family word of your own choosing (any concrete noun: *exhibition, storefront, cockpit, recipe-card, playbill, field-manual*). If any two variants share a label, or if the label could apply to the other variants equally well, rework. Do not use a fixed vocabulary list for the labels. *This pass is non-negotiable in departure mode and catches the monoculture failure that the sentence pass misses.*
|
||||
1. **Family pass.** Give each variant a concrete family label of your own choosing. If two variants share a label, or a label fits another variant equally well, rework. Do not use a fixed vocabulary. *This pass is non-negotiable in departure mode and catches monoculture the sentence pass misses.*
|
||||
2. **Sentence pass.** Write three one-sentence descriptions side by side. If two of them rhyme ("both feature big type" / "both are stacks of sections" / "both center the CTA"), rework the offender.
|
||||
|
||||
**When the primary axis is color or theme, forbid the trio from sharing theme + dominant hue.** Two dark-plus-one-dark is not distinct. Aim for three color worlds, not three shades of the same.
|
||||
|
||||
+58
-44
@@ -1,12 +1,13 @@
|
||||
# New visual work
|
||||
|
||||
This flow owns two decisions: the durable visual world when authority is absent, expanding, or explicitly replaced; and the task-scoped concept for the surface being made. PRODUCT.md owns product truth, DESIGN.md confirmed visual truth, and `.impeccable/surfaces/` the task strategy. Complete [init.md](init.md) first when PRODUCT.md is missing. Missing DESIGN.md does not route back to init.
|
||||
This flow owns the durable visual world when authority is absent, expanding, or explicitly replaced, plus only as much task-level shaping as the requested scope needs. PRODUCT.md owns product truth, DESIGN.md confirmed visual truth, and `.impeccable/surfaces/` durable task strategy. Complete [init.md](init.md) first when PRODUCT.md is missing. Missing DESIGN.md does not route back to init.
|
||||
|
||||
## 1. Name the intent
|
||||
|
||||
- **Greenfield:** no coherent visual implementation.
|
||||
- **Extension:** a new surface inside an established world.
|
||||
- **Expression expansion:** an established brand entering an unresolved surface family.
|
||||
- **Local extension:** a section, feature, component, or state inside an established surface and world.
|
||||
- **New surface:** a whole page, route, screen, flow, or standalone experience inside an established world.
|
||||
- **Expression expansion:** an established brand entering an unresolved whole-surface family or app boundary.
|
||||
- **Redesign/rebrand:** replace the world while preserving unchanged product truth, content, function, native affordances, constraints, and brand commitments.
|
||||
- **Refinement:** leave this flow for the scoped command; preserve the world and scope.
|
||||
|
||||
@@ -28,36 +29,17 @@ Use its invariants and normative tokens. Skip world-building and discover the su
|
||||
|
||||
Code, assets, tokens, type, and component behavior are incumbent authority. Run [document.md](document.md) in scan mode and confirm extracted invariants before writing DESIGN.md. Do not offer replacement worlds unless the user asked for a redesign.
|
||||
|
||||
### D. The brand exists, but this surface family is unresolved
|
||||
### D. The brand exists, but a whole-surface family is unresolved
|
||||
|
||||
Preserve logo, color/type assets, voice, recognizable component/motion traits, and constraints. Ask what must carry and where expression may expand. Offer two or three compatible ranges, not replacement identities, and merge the choice into DESIGN.md. Use a child-app DESIGN.md when the range is local.
|
||||
|
||||
A section, feature, component, or state inside a coherent existing surface stays on B or C. Its surrounding surface is authority even when DESIGN.md is incomplete.
|
||||
|
||||
### E. No confirmed visual authority exists
|
||||
|
||||
Establish a world. Scaffolds, framework defaults, and stray utilities are not identity.
|
||||
|
||||
## 3. Establish or replace the visual world
|
||||
|
||||
Run this only for A or E. The world must govern more than one artifact and still constrain the build.
|
||||
|
||||
1. **Ground.** Use PRODUCT.md's mechanism, users, context, evidence, commitments, and the brief. Ask at most three questions about unknown visual premises, never CSS values.
|
||||
2. **Derive.** Generate five to seven grounded candidates. State each identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, and reusable signature. Do not rank yet.
|
||||
3. **Add external selection pressure.** Run `node {{scripts_path}}/concept-seed.mjs --scope world`. Promote the assigned grounded candidate into the serious shortlist and weigh the printed challengers only when they can become a coherent system rather than a one-page costume.
|
||||
4. **Test breadth and defaults.** Reject one-hero costumes. Test navigation, quiet/dense content, interaction/state, and an unlike surface. Compare survivors with the category's habitual and predictable contrarian looks; revise defaults without turning anti-reference into recipe.
|
||||
5. **Offer neutral choices.** Present two or three materially different worlds without recommendation cues. Explain consequences; ask what is closest, should combine, or feels wrong. Rejection is allowed.
|
||||
6. **Resolve.** Set durable type, color roles, materials, layout, imagery, motion, and signature. Defer exact files and tokens when implementation is the honest decision point.
|
||||
|
||||
Without an answer mechanism, use the assigned grounded candidate only if it survives product fit and breadth; mark assumptions. This is fallback, not user choice.
|
||||
|
||||
### Write the directional DESIGN.md seed
|
||||
|
||||
Before code, write or replace DESIGN.md at the resolved project/app boundary using the [format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Record the chosen overview and relevant visual sections. Add:
|
||||
|
||||
`<!-- STATUS: DIRECTIONAL SEED; exact tokens remain provisional until the first implementation pass. -->`
|
||||
|
||||
Do not fabricate YAML tokens; add exact values only after the user, assets, or implementation establishes them. A redesign plus the user's world choice authorizes replacement without another confirmation.
|
||||
|
||||
## 4. Discover the requested surface
|
||||
## 3. Discover the requested surface
|
||||
|
||||
Name this surface's audience, job, visitor mode, real content, primary action/task, evidence, constraints, and memorable moment. PRODUCT.md supplies truth and DESIGN.md the world; neither decides narrative or composition.
|
||||
|
||||
@@ -65,25 +47,55 @@ Ask one attended round of at most three material questions without repeating dur
|
||||
|
||||
When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again.
|
||||
|
||||
## 5. Develop the surface concept
|
||||
Classify the scope before ideation. A section, feature, component, or state that must join an existing page is a local extension. A page, route, screen, flow, or standalone experience may be a new surface. Do not inflate local work into a surface concept merely because it benefits from a novel layout.
|
||||
|
||||
## 4. Shape or select the direction
|
||||
|
||||
Do not select a new world and its first surface concept in separate tournaments. That creates a safe global choice followed by a more interesting local choice whose “lineage” exists only in prose.
|
||||
|
||||
### Local extension inside stable authority: shape, do not seed
|
||||
|
||||
For a local extension on path B or C, inherit both the visual world and the surrounding surface's direction. Resolve only the decisions the addition actually introduces: purpose, content, hierarchy, state or interaction, and how it joins the existing sequence. Use short, related question rounds when those decisions are still open. Do not run `concept-seed.mjs`, generate competing surface metaphors, or offer alternate visual worlds. The result may still have an authored, surprising layout; its novelty must come from the material and the established grammar, not a new identity thesis.
|
||||
|
||||
If a local request reveals a genuine gap in the brand system, name that gap and ask before treating it as path D. Do not silently turn a case-study section or feature into an expression-expansion exercise.
|
||||
|
||||
### A, D, or E: choose a coupled world and first expression
|
||||
|
||||
For a replacement world, unresolved brand expansion, or no authority, make one coupled decision:
|
||||
|
||||
1. **Ground.** Use PRODUCT.md, incumbent commitments that still bind, and the surface discovery above. Name what this surface uniquely does, proves, or enables.
|
||||
2. **Derive pairs.** Generate five to seven grounded directions and order them by product fit. Each joins a durable visual system to a concrete first-surface structure, focal moment, and implementation consequence. Different names or materials on the same experience are one candidate, not several.
|
||||
3. **Break the ranking rut once.** Run `node {{scripts_path}}/concept-seed.mjs --scope direction`. Promote the assigned grounded pair. Translate each challenger into a coherent system and task solution before comparing it on audience identification and product clarity.
|
||||
4. **Test at full strength.** Strip names and styling; survivors must still differ in structure, sequence, or interaction. Reject a pair if its surface swaps into another unchanged, its world cannot govern unlike future surfaces, or a competent default could satisfy its focal moment. A candidate's risk must be a real tradeoff, not a reason it violates the brief.
|
||||
5. **Offer coupled choices.** Present two or three equally viable pairs without recommendation cues. For each, show the world rules, first-surface expression, cross-surface consequence, and risk. Ask what is closest, should combine, or feels wrong; rejection is allowed.
|
||||
6. **Resolve once.** The user selects or revises the pair. Extract the durable rules into DESIGN.md and the task-specific strategy into the surface brief; do not reopen either half independently.
|
||||
|
||||
Without an answer mechanism, use the assigned grounded pair only if it survives product fit, coupling, and breadth; mark assumptions. This is fallback, not user choice.
|
||||
|
||||
### B or C, whole surface only: choose a direction inside stable authority
|
||||
|
||||
The visual world supplies the vocabulary; the task concept supplies the sentence.
|
||||
|
||||
1. **State the mechanism.** Name what this surface does, proves, or enables that a neighbor could not truthfully claim.
|
||||
2. **Derive structural material.** From content, mechanism, audience, and DESIGN.md, list five to seven forms, behaviors, spatial arrangements, or narratives. Translate relationships and reading order, not costume. Do not rank yet.
|
||||
3. **Break the ranking rut.** For substantial greenfield, redesign, expression expansion, or extension work, run `node {{scripts_path}}/concept-seed.mjs --scope surface`. Promote its assigned grounded candidate and weigh challengers on audience identification and product clarity. Skip the roll for a small extension or a user-pinned concept.
|
||||
4. **Audit defaults.** Name the habitual arrangement and predictable contrarian response. Judge the shortlist skin-blind: without color, type, texture, or concept nouns, distinct candidates still differ in topology, sequence, or interaction.
|
||||
5. **Offer neutral choices.** Present two or three concepts without recommendation cues. Give each thesis, sequence, focal moment, signature, implementation consequence, and world lineage.
|
||||
6. **Let the user direct.** Ask what is closest, should combine, or feels wrong. Resolve before code; rejection is allowed.
|
||||
7. **Probe when useful.** For a substantial high-fidelity surface with native image generation, load [codex.md](codex.md). Probes stay inside the world.
|
||||
1. Derive five to seven structural candidates from the content, mechanism, audience, and confirmed authority. Translate its relationships and behavior, not just its styling.
|
||||
2. Run `node {{scripts_path}}/concept-seed.mjs --scope surface` only when a whole page, route, screen, flow, or standalone experience calls for high-concept exploration. Otherwise shape the strongest grounded structure directly. Never run it for a local extension.
|
||||
3. Name the habitual arrangement and predictable contrarian response. Judge candidates skin-blind: topology, sequence, or interaction must remain different after names and styling disappear.
|
||||
4. When materially different whole-surface choices would help, present two or three neutral options with thesis, sequence, focal moment, signature, implementation consequence, and concrete inherited world rules. Let the user select or revise before code.
|
||||
|
||||
Without an answer mechanism, use the assigned grounded concept only if it survives both tests.
|
||||
Without an answer mechanism, use the promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests.
|
||||
|
||||
For `shape`, stop after the user selects the concept and continue in [shape.md](shape.md). Keep a newly written DESIGN.md seed directional; exact tokens wait for implementation.
|
||||
For a substantial high-fidelity surface with native image generation, load [codex.md](codex.md) after selection. Probes stay inside the selected direction. For `shape`, stop after selection and continue in [shape.md](shape.md).
|
||||
|
||||
## 6. Persist the surface brief
|
||||
### Write or update DESIGN.md
|
||||
|
||||
Once the primary target or route is known, persist task-local product/UX strategy separately from PRODUCT.md and DESIGN.md. Prefer a clone-stable source file; map routes and alternate entries as related targets. Read any record first:
|
||||
For A or E, write or replace DESIGN.md at the resolved project/app boundary using the [format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). For D, merge only the approved expansion range. Record the chosen durable rules and add:
|
||||
|
||||
`<!-- STATUS: DIRECTIONAL SEED; exact tokens remain provisional until the first implementation pass. -->`
|
||||
|
||||
Do not fabricate YAML tokens; add exact values only after the user, assets, or implementation establishes them. The selected pair authorizes its world and first expression together without another confirmation. A local extension does not change DESIGN.md unless the user approves a durable system addition.
|
||||
|
||||
## 5. Persist the surface brief
|
||||
|
||||
Once the primary target or route is known, persist durable surface-level product/UX strategy separately from PRODUCT.md and DESIGN.md. Prefer a clone-stable source file; map routes and alternate entries as related targets. For a local extension, update the parent surface's record only when the work establishes durable product strategy; do not create a component-level brief by reflex. Read any record first:
|
||||
|
||||
`node {{scripts_path}}/surface-brief.mjs read <primary-target>`
|
||||
|
||||
@@ -112,7 +124,7 @@ The body is concise and contains:
|
||||
|
||||
Commit `.impeccable/surfaces/<target-slug>.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs.
|
||||
|
||||
## 7. Write the direction contract
|
||||
## 6. Write the direction contract for a whole surface
|
||||
|
||||
If a competent default could satisfy the concept, sharpen its focal moment until one product-specific move changes implementation. Difficulty must clarify the product, not add spectacle.
|
||||
|
||||
@@ -127,9 +139,11 @@ Before code, write a direction contract of at most 150 words in an opening HTML
|
||||
|
||||
The contract is task-scoped, inspectable, and subordinate to the user's choice. Put the same six blocks in the surface brief and artifact comment. <!-- rule:skill-decide-then-build -->
|
||||
|
||||
## 8. Plan, build, and commit
|
||||
A local extension skips this contract unless the user explicitly wants it to become a distinct authored moment. Use the shaped decisions as the implementation plan instead.
|
||||
|
||||
Plan from the concept and real content, never a category skeleton. In redesign, remove inherited visual tokens.
|
||||
## 7. Plan, build, and commit
|
||||
|
||||
Plan from the selected direction or local shape and real content, never a category skeleton. In redesign, remove inherited visual tokens.
|
||||
|
||||
Load only needed specialist references. Focal interaction or authored animation reads [animate.md](animate.md), even without the `animate` command.
|
||||
|
||||
@@ -147,7 +161,7 @@ Build the strongest coherent direction once. Its grammar governs navigation, act
|
||||
|
||||
Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama.
|
||||
|
||||
## 9. Solidify the visual record
|
||||
## 8. Solidify the visual record
|
||||
|
||||
After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build:
|
||||
|
||||
@@ -159,6 +173,6 @@ After first implementation of a new/replacement world or approved expansion, ref
|
||||
|
||||
Ordinary extension does not rewrite DESIGN.md; only approved durable changes do.
|
||||
|
||||
## 10. Finish like a studio
|
||||
## 9. Finish like a studio
|
||||
|
||||
Inspect desktop and mobile; critique against the brief, DESIGN.md, concept, and contract; patch material defects; recheck skin-blind; run the detector once. With a Stop hook, fix real gaps and classify false positives until none remain. Add a reviewer only when risk earns it. <!-- rule:skill-finish-like-studio -->
|
||||
Inspect desktop and mobile; critique against the brief, DESIGN.md, and the applicable shape, concept, or contract; patch material defects; recheck skin-blind. Follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add another detector pass. Fix real gaps and classify false positives until none remain. Add a reviewer only when risk earns it. <!-- rule:skill-finish-like-studio -->
|
||||
|
||||
@@ -4,7 +4,7 @@ When design SERVES the product: app UIs, admin dashboards, settings panels, data
|
||||
|
||||
## The product slop test
|
||||
|
||||
Not "would someone say AI made this." Familiarity is often a feature here. The test is: would a user fluent in the category's best tools (Linear, Figma, Notion, Raycast, Stripe come to mind) sit down and trust this interface, or pause at every subtly-off component?
|
||||
Familiarity is often a feature here. The test is whether a category-fluent user can trust the interface immediately or must pause at every subtly-off component.
|
||||
|
||||
Product UI's failure mode isn't flatness, it's strangeness without purpose: over-decorated buttons, mismatched form controls, gratuitous motion, display fonts where labels should be, invented affordances for standard tasks. The bar is earned familiarity. The tool should disappear into the task.
|
||||
|
||||
@@ -41,7 +41,7 @@ Every interactive component has: default, hover, focus, active, disabled, loadin
|
||||
- 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)
|
||||
## Product constraints
|
||||
|
||||
- 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 -->
|
||||
@@ -54,7 +54,7 @@ Every interactive component has: default, hover, focus, active, disabled, loadin
|
||||
|
||||
Product can afford things brand surfaces can't.
|
||||
|
||||
- System fonts and familiar sans defaults (Inter, SF Pro, system-ui stacks).
|
||||
- System fonts and familiar sans defaults.
|
||||
- Standard navigation patterns: top bar + side nav, breadcrumbs, tabs, command palettes.
|
||||
- Density. Tables with many rows, panels with many labels, dense information when users need it.
|
||||
- Consistency over surprise. The same visual vocabulary screen to screen is a virtue; delight is saved for moments, not pages.
|
||||
|
||||
@@ -93,6 +93,6 @@ Walk the complete path again with mouse, keyboard, and touch where applicable. C
|
||||
- console errors, layout shift, interaction latency, image loading, and supported browsers;
|
||||
- agreement with DESIGN.md, neighboring features, and the user's scope.
|
||||
|
||||
Run the relevant detector or QA commands, fix real defects, and document only narrow intentional exceptions. A clean scan does not replace visual judgment.
|
||||
Follow the quality guidance supplied by `context.mjs` and hooks, then run any other relevant QA commands. Context requests a manual scan only when no automatic detector is active; never add another detector pass. Fix real defects and document only narrow intentional exceptions. A clean scan does not replace visual judgment.
|
||||
|
||||
Finish with a source diff: remove accidental churn, orphaned code, redundant values, and temporary artifacts. Ship only when the feature is functionally complete and consistently finished across the path.
|
||||
|
||||
+47
-268
@@ -1,301 +1,80 @@
|
||||
Typography carries most of the information on the page. Replace generic defaults (Inter, Roboto, system fallback at flat scale) with type that reflects the brand and scales with intentional contrast.
|
||||
Typography carries information, hierarchy, and voice. Improve it inside the established visual world; do not replace the identity unless the user asked to.
|
||||
|
||||
---
|
||||
|
||||
## Visitor mode
|
||||
|
||||
New or replacement identity work belongs to [new-work.md](new-work.md), which establishes typography direction with the rest of DESIGN.md. This command works from that committed world. If the user explicitly wants to replace its typographic identity, route the identity change through new-work and update DESIGN.md; otherwise improve hierarchy, scale, measure, weights, and pairing inside the existing direction. Fluid `clamp()` scale and a ≥1.25 ratio between display steps are useful starting points for Persuade and Experience, not universal mandates.
|
||||
- **Persuade + Experience:** display type may carry the voice. Use decisive contrast and responsive scale when the composition benefits.
|
||||
- **Operate + Read:** stability, scanability, and measure come first. A single well-tuned family and fixed role scale are often right.
|
||||
- **Native:** follow [ios.md](ios.md) or [android.md](android.md), including platform scaling and accessibility behavior.
|
||||
|
||||
Operate + Read: system fonts and familiar sans stacks are legitimate here. One well-tuned family typically carries the whole UI. Fixed `rem` scale, 1.125–1.2 ratio between more closely-spaced steps. Long-form Read content wants a steady reading measure and a quiet, stable scale, not display-scale drama.
|
||||
If typography replacement would create a new identity, route through [new-work.md](new-work.md) and update DESIGN.md. Otherwise preserve confirmed families and improve their use.
|
||||
|
||||
---
|
||||
## Two isolated assessments
|
||||
|
||||
## Two isolated assessments (required)
|
||||
When a sub-agent tool is available and permitted, run these independently; otherwise run them yourself in this order. Do not let detector findings anchor the design assessment.
|
||||
|
||||
Spawn two parallel sub-agents whenever a sub-agent/Task tool is exposed: one for the typography assessment, one for the mechanical pre-scan. If the harness needs explicit user permission for sub-agents, stop and ask before proceeding. Isolation is the point: detector output anchors visual judgment toward what the scan can see, so neither sub-agent gets the other's output. Each assessment runs in its own sub-agent; running either one in this context when a sub-agent tool exists is not permitted, even when it is faster; the fallback below is only for sessions with no sub-agent tool. Give each a self-contained prompt (target files, visitor mode, **DESIGN.md** content when present, and its instructions below); do not assume it can read this file.
|
||||
|
||||
**Sub-agent A (typography assessment)**: give it the full [Assess Current Typography](#assess-current-typography) checklist below, verbatim, in its prompt. It works through every item and returns per-item findings citing file, selector, or value.
|
||||
|
||||
**Sub-agent B (mechanical pre-scan)**: run the bundled detector scoped to type:
|
||||
1. **Typographic assessment:** inspect representative pages and styles. Answer every question below with a file, selector, or computed value:
|
||||
- **Authority and fit:** Which faces, weights, and roles are established? Do they fit the product and selected world, or are they unexamined defaults? Is every family necessary?
|
||||
- **Hierarchy:** Can heading, body, label, metadata, and data roles be distinguished at a glance? Are adjacent sizes or weights too close to carry different jobs?
|
||||
- **Scale and consistency:** Is there a deliberate role scale, or a collection of arbitrary values? Do repeated roles stay identical across screens and states?
|
||||
- **Reading:** Does body copy stay within a comfortable 45–75 character measure? Are line height, paragraph rhythm, contrast, and tracking tuned to the actual face, width, language, and surface?
|
||||
- **Stress:** What happens with long headings, localization expansion, zoom, narrow containers, missing weights, and font fallback?
|
||||
- **Delivery:** Are only used assets loaded? Do fallback metrics, loading strategy, and variable-font settings avoid invisible text and disruptive reflow?
|
||||
2. **Mechanical scan:** run:
|
||||
|
||||
```bash
|
||||
node {{scripts_path}}/detect.mjs --json --scope type [target files or dirs]
|
||||
```
|
||||
|
||||
A missing `node` on PATH is not permission to skip: hunt for a runtime (`command -v node`, nvm or Homebrew paths, the harness's own bundled node) and run it by full path. If none exists, halt the scan and report that Node must be installed (the parent relays this to the user); do **not** substitute grep for the detector or proceed unscanned. The scan checks literal font sizes against the **DESIGN.md** ramp but abstains on `em`, `%`, `clamp()`, and line-heights, so also grep `font-size\s*:`, `fontSize`, `text-\[`, `leading-\[` and judge those hits against the spec. Return the findings JSON plus the grep verdicts.
|
||||
Also inspect dynamic or arbitrary font values the detector cannot interpret. Synthesize both assessments before editing, noting what each caught alone. A clean scan is a floor, not proof of good typography.
|
||||
|
||||
**If no sub-agent tool is exposed (or the user declined)**: run both yourself, assessment first, pre-scan second, so the deterministic findings can't anchor the visual judgment. Keep that order even when the scan feels quicker to start with.
|
||||
## Set the system
|
||||
|
||||
**Synthesize** once both are done: merge into a single findings list, noting where they agree and what each caught alone. Fix every finding, or list it as a deliberate exception for the user to accept. A clean scan is a floor, not a verdict: a generic font stack at a flat scale passes every detector rule, which is exactly what the assessment exists to catch. State in your final summary which path ran (parallel sub-agents or single-context fallback).
|
||||
Before editing, state:
|
||||
|
||||
---
|
||||
- the roles the interface needs;
|
||||
- the intended contrast between those roles;
|
||||
- the reading measure and density;
|
||||
- which existing faces and weights are authoritative;
|
||||
- any performance, localization, or accessibility constraints.
|
||||
|
||||
## Assess Current Typography
|
||||
Use the fewest roles and families that make the hierarchy unmistakable. Combine size, weight, space, and tone deliberately instead of asking size alone to do all the work. Role names and tokens should describe purpose rather than values.
|
||||
|
||||
This checklist is sub-agent A's brief (on the fallback path, work through it yourself before the pre-scan). Analyze what's weak or generic about the current type:
|
||||
## Apply
|
||||
|
||||
1. **Font choices**:
|
||||
- Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
|
||||
- Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
|
||||
- Are there too many font families? (More than 2-3 is almost always a mess)
|
||||
- Keep body copy comfortably readable and zoomable. Use 1rem / 16px as the ordinary web body floor unless a dense role, platform convention, or user setting justifies otherwise.
|
||||
- Keep prose in the 45–75ch range. Tune line height inversely with measure: wider lines generally need more leading.
|
||||
- Compensate light text on dark surfaces on all three perceptual axes: slightly more line height, a touch more tracking, and one step more weight when the face needs it.
|
||||
- Tune line height to the face, width, language, and contrast, not a universal ratio.
|
||||
- Keep repeated roles consistent across screens and states.
|
||||
- Use numeric, tabular, code, and label features when their content benefits.
|
||||
- Load only used font assets and weights. Provide metric-compatible fallbacks and avoid blocking text.
|
||||
- Let marketing display type respond to available space when useful; keep dense product and reading surfaces spatially predictable.
|
||||
- Preserve browser zoom, user font settings, Dynamic Type, and platform text scaling.
|
||||
- Use paragraph spacing or first-line indentation as the primary paragraph rhythm; combining both usually double-marks the boundary.
|
||||
|
||||
2. **Hierarchy**:
|
||||
- Can you tell headings from body from captions at a glance?
|
||||
- Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
|
||||
- Are weight contrasts strong enough? (Medium vs Regular is barely visible)
|
||||
Do not make type decorative at the expense of comprehension, or introduce a second family without a clear role it alone can perform.
|
||||
|
||||
3. **Sizing & scale**:
|
||||
- Is there a consistent type scale, or are sizes arbitrary?
|
||||
- Does body text meet minimum readability? (16px+)
|
||||
- Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs and long-form reading, where a steady measure beats display scale; fluid `clamp()` for marketing-page headings)
|
||||
## Verify
|
||||
|
||||
4. **Readability**:
|
||||
- Are line lengths comfortable? (45-75 characters ideal)
|
||||
- Is line-height appropriate for the font and context?
|
||||
- Is there enough contrast between text and background?
|
||||
- Primary, secondary, body, and metadata roles are recognizable without reading the copy.
|
||||
- Long text remains comfortable across relevant widths and languages.
|
||||
- The typography belongs to the product and its established world.
|
||||
- Loading does not create disruptive reflow or invisible text.
|
||||
- Zoom, text scaling, focus, contrast, and reduced viewport paths remain usable.
|
||||
- The final mechanical scan has no unexplained findings.
|
||||
|
||||
5. **Consistency**:
|
||||
- Are the same elements styled the same way throughout?
|
||||
- Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
|
||||
- Is letter-spacing intentional or default everywhere?
|
||||
Answer each item with rendered or source evidence, then rerun the scan. Do not substitute a bare “yes” for verification.
|
||||
|
||||
**CRITICAL**: The goal isn't to make text "fancier." It's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
|
||||
|
||||
## Plan Typography Improvements
|
||||
|
||||
Consult the [Reference Material](#reference-material) section below for detailed guidance on scales, pairing, and loading strategies.
|
||||
|
||||
Create a systematic plan:
|
||||
|
||||
- **Font selection**: Do fonts need replacing? What fits the brand/context?
|
||||
- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
|
||||
- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings, or whatever fits)
|
||||
- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
|
||||
|
||||
## Improve Typography Systematically
|
||||
|
||||
### Font Selection
|
||||
|
||||
If fonts need replacing:
|
||||
- Choose fonts that reflect the brand personality
|
||||
- Pair with genuine contrast (serif + sans, geometric + humanist), or use a single family in multiple weights
|
||||
- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
|
||||
|
||||
### Establish Hierarchy
|
||||
|
||||
Build a clear type scale:
|
||||
- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
|
||||
- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
|
||||
- **Combine dimensions**: Size + weight + color + space for strong hierarchy. Don't rely on size alone
|
||||
- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
|
||||
- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
|
||||
|
||||
### Fix Readability
|
||||
|
||||
- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
|
||||
- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
|
||||
- Increase line-height slightly for light-on-dark text
|
||||
- Ensure body text is at least 16px / 1rem
|
||||
|
||||
### Refine Details
|
||||
|
||||
- Use `tabular-nums` for data tables and numbers that should align
|
||||
- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
|
||||
- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
|
||||
- Set `font-kerning: normal` and consider OpenType features where appropriate
|
||||
|
||||
### Weight Consistency
|
||||
|
||||
- Define clear roles for each weight and stick to them
|
||||
- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
|
||||
- Load only the weights you actually use (each weight adds to page load)
|
||||
|
||||
**NEVER**:
|
||||
- Use more than 2-3 font families
|
||||
- Pick sizes arbitrarily; commit to a scale
|
||||
- Set body text below 16px
|
||||
- Use decorative/display fonts for body text
|
||||
- Disable browser zoom (`user-scalable=no`)
|
||||
- Use `px` for font sizes; use `rem` to respect user settings
|
||||
- Default to Inter/Roboto/Open Sans when personality matters
|
||||
- Pair fonts that are similar but not identical (two geometric sans-serifs)
|
||||
|
||||
## Verify Typography Improvements
|
||||
|
||||
- **Hierarchy**: Can you identify heading vs body vs caption instantly?
|
||||
- **Readability**: Is body text comfortable to read in long passages?
|
||||
- **Consistency**: Are same-role elements styled identically throughout?
|
||||
- **Personality**: Does the typography reflect the brand?
|
||||
- **Performance**: Are web fonts loading efficiently without layout shift?
|
||||
- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
|
||||
|
||||
Answer each item above by citing the file, selector, or value that satisfies it; never a bare yes. Then re-run the pre-scan and fix until the count of unresolved items and unaccepted findings is zero.
|
||||
|
||||
When the type carries the hierarchy on its own, hand off to `{{command_prefix}}impeccable polish` for the final pass.
|
||||
When the hierarchy holds, hand off to `{{command_prefix}}impeccable polish`.
|
||||
|
||||
## Live-mode signature params
|
||||
|
||||
Each variant MUST declare a `scale` param controlling the hierarchy ratio. Express all font sizes in the variant's scoped CSS through `calc(var(--p-scale, 1) * <base>)` or, better, scale the type ramp via `clamp(min, calc(var(--p-scale, 1) * Npx), max)`. Users slide from subdued to commanding.
|
||||
Every variant declares a coarse `scale` parameter and authors its type ramp against `var(--p-scale, 1)`.
|
||||
|
||||
```json
|
||||
{"id":"scale","kind":"range","min":0.85,"max":1.3,"step":0.05,"default":1,"label":"Scale"}
|
||||
```
|
||||
|
||||
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.05–0.1, add a touch of letter-spacing (0.01–0.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
|
||||
|
||||
When a typeface change is actually authorized by the brief and DESIGN.md, use the anti-reflex corrections and pairing principles below. For a broader identity replacement, return to [new-work.md](new-work.md) instead of letting a typography command silently create a parallel world.
|
||||
|
||||
##### 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 1–2 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 45–75ch 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 5–12% 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.
|
||||
Add at most one pairing or weight parameter when it represents a real system choice. Follow [live.md](live.md)'s parameter contract.
|
||||
|
||||
Reference in New Issue
Block a user